diff --git a/example/main.tf b/example/main.tf index d4f7d298..eb6844cc 100644 --- a/example/main.tf +++ b/example/main.tf @@ -444,3 +444,80 @@ resource keycloak_hardcoded_attribute_identity_provider_mapper saml { attribute_value = "value" user_session = false } + +data "keycloak_openid_client" "broker" { + realm_id = "${keycloak_realm.test.id}" + client_id = "broker" +} + +data "keycloak_openid_client_authorization_policy" "default" { + realm_id = "${keycloak_realm.test.id}" + resource_server_id = "${keycloak_openid_client.test_client_auth.resource_server_id}" + name = "default" +} + +resource "keycloak_openid_client" "test_client_auth" { + client_id = "test-client-auth" + name = "test-client-auth" + realm_id = "${keycloak_realm.test.id}" + description = "a test openid client" + + access_type = "CONFIDENTIAL" + direct_access_grants_enabled = true + implicit_flow_enabled = true + service_accounts_enabled = true + + valid_redirect_uris = [ + "http://localhost:5555/callback", + ] + + authorization { + policy_enforcement_mode = "ENFORCING" + } + + client_secret = "secret" +} + +resource "keycloak_openid_client_authorization_permission" "resource" { + resource_server_id = "${keycloak_openid_client.test_client_auth.resource_server_id}" + realm_id = "${keycloak_realm.test.id}" + name = "test" + policies = ["${data.keycloak_openid_client_authorization_policy.default.id}"] + resources = ["${keycloak_openid_client_authorization_resource.resource.id}"] +} + +resource "keycloak_openid_client_authorization_resource" "resource" { + resource_server_id = "${keycloak_openid_client.test_client_auth.resource_server_id}" + name = "test-openid-client1" + realm_id = "${keycloak_realm.test.id}" + + uris = [ + "/endpoint/*" + ] + + attributes = { + "asdads" = "asdasd" + } +} + +resource "keycloak_openid_client_authorization_scope" "resource" { + resource_server_id = "${keycloak_openid_client.test_client_auth.resource_server_id}" + name = "test-openid-client1" + realm_id = "${keycloak_realm.test.id}" +} + +resource "keycloak_user" "resource" { + realm_id = "${keycloak_realm.test.id}" + username = "test" + + attributes = { + "key" = "value" + } +} + +resource "keycloak_openid_client_service_account_role" "read_token" { + realm_id = "${keycloak_realm.test.id}" + client_id = "${data.keycloak_openid_client.broker.id}" + service_account_user_id = "${keycloak_openid_client.test_client_auth.service_account_user_id}" + role = "read-token" +} diff --git a/keycloak/custom_user_federation.go b/keycloak/custom_user_federation.go index 20c6f19f..9ebf1e40 100644 --- a/keycloak/custom_user_federation.go +++ b/keycloak/custom_user_federation.go @@ -85,7 +85,7 @@ func (keycloakClient *KeycloakClient) ValidateCustomUserFederation(custom *Custo } func (keycloakClient *KeycloakClient) NewCustomUserFederation(customUserFederation *CustomUserFederation) error { - location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/components", customUserFederation.RealmId), convertFromCustomUserFederationToComponent(customUserFederation)) + _, location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/components", customUserFederation.RealmId), convertFromCustomUserFederationToComponent(customUserFederation)) if err != nil { return err } @@ -98,7 +98,7 @@ func (keycloakClient *KeycloakClient) NewCustomUserFederation(customUserFederati func (keycloakClient *KeycloakClient) GetCustomUserFederation(realmId, id string) (*CustomUserFederation, error) { var component *component - err := keycloakClient.get(fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component, nil) if err != nil { return nil, err } @@ -111,5 +111,5 @@ func (keycloakClient *KeycloakClient) UpdateCustomUserFederation(customUserFeder } func (keycloakClient *KeycloakClient) DeleteCustomUserFederation(realmId, id string) error { - return keycloakClient.delete(fmt.Sprintf("/realms/%s/components/%s", realmId, id)) + return keycloakClient.delete(fmt.Sprintf("/realms/%s/components/%s", realmId, id), nil) } diff --git a/keycloak/generic_client.go b/keycloak/generic_client.go index 5f97220b..6c98ac64 100644 --- a/keycloak/generic_client.go +++ b/keycloak/generic_client.go @@ -16,7 +16,7 @@ type GenericClient struct { func (keycloakClient *KeycloakClient) listGenericClients(realmId string) ([]*GenericClient, error) { var clients []*GenericClient - err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients", realmId), &clients) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients", realmId), &clients, nil) if err != nil { return nil, err } diff --git a/keycloak/group.go b/keycloak/group.go index 370e028c..8418f303 100644 --- a/keycloak/group.go +++ b/keycloak/group.go @@ -2,7 +2,6 @@ package keycloak import ( "fmt" - "net/url" "strings" ) @@ -79,7 +78,7 @@ func (keycloakClient *KeycloakClient) NewGroup(group *Group) error { createGroupUrl = fmt.Sprintf("/realms/%s/groups/%s/children", group.RealmId, group.ParentId) } - location, err := keycloakClient.post(createGroupUrl, group) + _, location, err := keycloakClient.post(createGroupUrl, group) if err != nil { return err } @@ -92,7 +91,7 @@ func (keycloakClient *KeycloakClient) NewGroup(group *Group) error { func (keycloakClient *KeycloakClient) GetGroup(realmId, id string) (*Group, error) { var group Group - err := keycloakClient.get(fmt.Sprintf("/realms/%s/groups/%s", realmId, id), &group) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/groups/%s", realmId, id), &group, nil) if err != nil { return nil, err } @@ -114,13 +113,17 @@ func (keycloakClient *KeycloakClient) UpdateGroup(group *Group) error { } func (keycloakClient *KeycloakClient) DeleteGroup(realmId, id string) error { - return keycloakClient.delete(fmt.Sprintf("/realms/%s/groups/%s", realmId, id)) + return keycloakClient.delete(fmt.Sprintf("/realms/%s/groups/%s", realmId, id), nil) } func (keycloakClient *KeycloakClient) ListGroupsWithName(realmId, name string) ([]*Group, error) { var groups []*Group - err := keycloakClient.get(fmt.Sprintf("/realms/%s/groups?search=%s", realmId, url.QueryEscape(name)), &groups) + params := map[string]string{ + "search": name, + } + + err := keycloakClient.get(fmt.Sprintf("/realms/%s/groups", realmId), &groups, params) if err != nil { return nil, err } @@ -131,7 +134,7 @@ func (keycloakClient *KeycloakClient) ListGroupsWithName(realmId, name string) ( func (keycloakClient *KeycloakClient) GetGroupMembers(realmId, groupId string) ([]*User, error) { var users []*User - err := keycloakClient.get(fmt.Sprintf("/realms/%s/groups/%s/members", realmId, groupId), &users) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/groups/%s/members", realmId, groupId), &users, nil) if err != nil { return nil, err } diff --git a/keycloak/identity_provider.go b/keycloak/identity_provider.go index f536d55b..32de6a54 100644 --- a/keycloak/identity_provider.go +++ b/keycloak/identity_provider.go @@ -53,7 +53,7 @@ type IdentityProvider struct { func (keycloakClient *KeycloakClient) NewIdentityProvider(identityProvider *IdentityProvider) error { log.Printf("[WARN] Realm: %s", identityProvider.Realm) - _, err := keycloakClient.post(fmt.Sprintf("/realms/%s/identity-provider/instances", identityProvider.Realm), identityProvider) + _, _, err := keycloakClient.post(fmt.Sprintf("/realms/%s/identity-provider/instances", identityProvider.Realm), identityProvider) if err != nil { return err } @@ -65,7 +65,7 @@ func (keycloakClient *KeycloakClient) GetIdentityProvider(realm, alias string) ( var identityProvider IdentityProvider identityProvider.Realm = realm - err := keycloakClient.get(fmt.Sprintf("/realms/%s/identity-provider/instances/%s", realm, alias), &identityProvider) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/identity-provider/instances/%s", realm, alias), &identityProvider, nil) if err != nil { return nil, err } @@ -78,5 +78,5 @@ func (keycloakClient *KeycloakClient) UpdateIdentityProvider(identityProvider *I } func (keycloakClient *KeycloakClient) DeleteIdentityProvider(realm, alias string) error { - return keycloakClient.delete(fmt.Sprintf("/realms/%s/identity-provider/instances/%s", realm, alias)) + return keycloakClient.delete(fmt.Sprintf("/realms/%s/identity-provider/instances/%s", realm, alias), nil) } diff --git a/keycloak/identity_provider_mapper.go b/keycloak/identity_provider_mapper.go index a90d5d76..f30927a6 100644 --- a/keycloak/identity_provider_mapper.go +++ b/keycloak/identity_provider_mapper.go @@ -29,7 +29,7 @@ type IdentityProviderMapper struct { func (keycloakClient *KeycloakClient) NewIdentityProviderMapper(identityProviderMapper *IdentityProviderMapper) error { log.Printf("[WARN] Realm: %s", identityProviderMapper.Realm) - location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/identity-provider/instances/%s/mappers", identityProviderMapper.Realm, identityProviderMapper.IdentityProviderAlias), identityProviderMapper) + _, location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/identity-provider/instances/%s/mappers", identityProviderMapper.Realm, identityProviderMapper.IdentityProviderAlias), identityProviderMapper) if err != nil { return err } @@ -44,7 +44,7 @@ func (keycloakClient *KeycloakClient) GetIdentityProviderMapper(realm, alias, id identityProviderMapper.Realm = realm identityProviderMapper.IdentityProviderAlias = alias - err := keycloakClient.get(fmt.Sprintf("/realms/%s/identity-provider/instances/%s/mappers/%s", realm, alias, id), &identityProviderMapper) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/identity-provider/instances/%s/mappers/%s", realm, alias, id), &identityProviderMapper, nil) if err != nil { return nil, err } @@ -57,5 +57,5 @@ func (keycloakClient *KeycloakClient) UpdateIdentityProviderMapper(identityProvi } func (keycloakClient *KeycloakClient) DeleteIdentityProviderMapper(realm, alias, id string) error { - return keycloakClient.delete(fmt.Sprintf("/realms/%s/identity-provider/instances/%s/mappers/%s", realm, alias, id)) + return keycloakClient.delete(fmt.Sprintf("/realms/%s/identity-provider/instances/%s/mappers/%s", realm, alias, id), nil) } diff --git a/keycloak/keycloak_client.go b/keycloak/keycloak_client.go index 91a6dfd6..f131a97f 100644 --- a/keycloak/keycloak_client.go +++ b/keycloak/keycloak_client.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "fmt" + "io" "io/ioutil" "log" "net/http" @@ -166,7 +167,7 @@ func (keycloakClient *KeycloakClient) addRequestHeaders(request *http.Request) { request.Header.Set("Authorization", fmt.Sprintf("%s %s", tokenType, accessToken)) request.Header.Set("Accept", "application/json") - if request.Method == http.MethodPost || request.Method == http.MethodPut { + if request.Method == http.MethodPost || request.Method == http.MethodPut || request.Method == http.MethodDelete { request.Header.Set("Content-type", "application/json") } } @@ -247,7 +248,7 @@ func (keycloakClient *KeycloakClient) sendRequest(request *http.Request) ([]byte return body, response.Header.Get("Location"), nil } -func (keycloakClient *KeycloakClient) get(path string, resource interface{}) error { +func (keycloakClient *KeycloakClient) get(path string, resource interface{}, params map[string]string) error { resourceUrl := keycloakClient.baseUrl + apiUrl + path request, err := http.NewRequest(http.MethodGet, resourceUrl, nil) @@ -255,6 +256,14 @@ func (keycloakClient *KeycloakClient) get(path string, resource interface{}) err return err } + if params != nil { + query := url.Values{} + for k, v := range params { + query.Add(k, v) + } + request.URL.RawQuery = query.Encode() + } + body, _, err := keycloakClient.sendRequest(request) if err != nil { return err @@ -263,22 +272,22 @@ func (keycloakClient *KeycloakClient) get(path string, resource interface{}) err return json.Unmarshal(body, resource) } -func (keycloakClient *KeycloakClient) post(path string, requestBody interface{}) (string, error) { +func (keycloakClient *KeycloakClient) post(path string, requestBody interface{}) ([]byte, string, error) { resourceUrl := keycloakClient.baseUrl + apiUrl + path payload, err := json.Marshal(requestBody) if err != nil { - return "", err + return nil, "", err } request, err := http.NewRequest(http.MethodPost, resourceUrl, bytes.NewReader(payload)) if err != nil { - return "", err + return nil, "", err } - _, location, err := keycloakClient.sendRequest(request) + body, location, err := keycloakClient.sendRequest(request) - return location, err + return body, location, err } func (keycloakClient *KeycloakClient) put(path string, requestBody interface{}) error { @@ -299,10 +308,20 @@ func (keycloakClient *KeycloakClient) put(path string, requestBody interface{}) return err } -func (keycloakClient *KeycloakClient) delete(path string) error { +func (keycloakClient *KeycloakClient) delete(path string, requestBody interface{}) error { resourceUrl := keycloakClient.baseUrl + apiUrl + path - request, err := http.NewRequest(http.MethodDelete, resourceUrl, nil) + var body io.Reader + + if requestBody != nil { + payload, err := json.Marshal(requestBody) + if err != nil { + return err + } + body = bytes.NewReader(payload) + } + + request, err := http.NewRequest(http.MethodDelete, resourceUrl, body) if err != nil { return err } diff --git a/keycloak/ldap_full_name_mapper.go b/keycloak/ldap_full_name_mapper.go index 68616f2b..bcf677ce 100644 --- a/keycloak/ldap_full_name_mapper.go +++ b/keycloak/ldap_full_name_mapper.go @@ -82,7 +82,7 @@ func (keycloakClient *KeycloakClient) ValidateLdapFullNameMapper(mapper *LdapFul } func (keycloakClient *KeycloakClient) NewLdapFullNameMapper(ldapFullNameMapper *LdapFullNameMapper) error { - location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/components", ldapFullNameMapper.RealmId), convertFromLdapFullNameMapperToComponent(ldapFullNameMapper)) + _, location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/components", ldapFullNameMapper.RealmId), convertFromLdapFullNameMapperToComponent(ldapFullNameMapper)) if err != nil { return err } @@ -95,7 +95,7 @@ func (keycloakClient *KeycloakClient) NewLdapFullNameMapper(ldapFullNameMapper * func (keycloakClient *KeycloakClient) GetLdapFullNameMapper(realmId, id string) (*LdapFullNameMapper, error) { var component *component - err := keycloakClient.get(fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component, nil) if err != nil { return nil, err } @@ -108,5 +108,5 @@ func (keycloakClient *KeycloakClient) UpdateLdapFullNameMapper(ldapFullNameMappe } func (keycloakClient *KeycloakClient) DeleteLdapFullNameMapper(realmId, id string) error { - return keycloakClient.delete(fmt.Sprintf("/realms/%s/components/%s", realmId, id)) + return keycloakClient.delete(fmt.Sprintf("/realms/%s/components/%s", realmId, id), nil) } diff --git a/keycloak/ldap_group_mapper.go b/keycloak/ldap_group_mapper.go index b08dc239..cc7d460d 100644 --- a/keycloak/ldap_group_mapper.go +++ b/keycloak/ldap_group_mapper.go @@ -153,7 +153,7 @@ func (keycloakClient *KeycloakClient) ValidateLdapGroupMapper(ldapGroupMapper *L } func (keycloakClient *KeycloakClient) NewLdapGroupMapper(ldapGroupMapper *LdapGroupMapper) error { - location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/components", ldapGroupMapper.RealmId), convertFromLdapGroupMapperToComponent(ldapGroupMapper)) + _, location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/components", ldapGroupMapper.RealmId), convertFromLdapGroupMapperToComponent(ldapGroupMapper)) if err != nil { return err } @@ -166,7 +166,7 @@ func (keycloakClient *KeycloakClient) NewLdapGroupMapper(ldapGroupMapper *LdapGr func (keycloakClient *KeycloakClient) GetLdapGroupMapper(realmId, id string) (*LdapGroupMapper, error) { var component *component - err := keycloakClient.get(fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component, nil) if err != nil { return nil, err } @@ -179,5 +179,5 @@ func (keycloakClient *KeycloakClient) UpdateLdapGroupMapper(ldapGroupMapper *Lda } func (keycloakClient *KeycloakClient) DeleteLdapGroupMapper(realmId, id string) error { - return keycloakClient.delete(fmt.Sprintf("/realms/%s/components/%s", realmId, id)) + return keycloakClient.delete(fmt.Sprintf("/realms/%s/components/%s", realmId, id), nil) } diff --git a/keycloak/ldap_msad_user_account_control_mapper.go b/keycloak/ldap_msad_user_account_control_mapper.go index d63f53a6..a2890bb7 100644 --- a/keycloak/ldap_msad_user_account_control_mapper.go +++ b/keycloak/ldap_msad_user_account_control_mapper.go @@ -46,7 +46,7 @@ func convertFromComponentToLdapMsadUserAccountControlMapper(component *component } func (keycloakClient *KeycloakClient) NewLdapMsadUserAccountControlMapper(ldapMsadUserAccountControlMapper *LdapMsadUserAccountControlMapper) error { - location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/components", ldapMsadUserAccountControlMapper.RealmId), convertFromLdapMsadUserAccountControlMapperToComponent(ldapMsadUserAccountControlMapper)) + _, location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/components", ldapMsadUserAccountControlMapper.RealmId), convertFromLdapMsadUserAccountControlMapperToComponent(ldapMsadUserAccountControlMapper)) if err != nil { return err } @@ -59,7 +59,7 @@ func (keycloakClient *KeycloakClient) NewLdapMsadUserAccountControlMapper(ldapMs func (keycloakClient *KeycloakClient) GetLdapMsadUserAccountControlMapper(realmId, id string) (*LdapMsadUserAccountControlMapper, error) { var component *component - err := keycloakClient.get(fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component, nil) if err != nil { return nil, err } @@ -72,5 +72,5 @@ func (keycloakClient *KeycloakClient) UpdateLdapMsadUserAccountControlMapper(lda } func (keycloakClient *KeycloakClient) DeleteLdapMsadUserAccountControlMapper(realmId, id string) error { - return keycloakClient.delete(fmt.Sprintf("/realms/%s/components/%s", realmId, id)) + return keycloakClient.delete(fmt.Sprintf("/realms/%s/components/%s", realmId, id), nil) } diff --git a/keycloak/ldap_user_attribute_mapper.go b/keycloak/ldap_user_attribute_mapper.go index 0d9d917f..3b164bcb 100644 --- a/keycloak/ldap_user_attribute_mapper.go +++ b/keycloak/ldap_user_attribute_mapper.go @@ -76,7 +76,7 @@ func convertFromComponentToLdapUserAttributeMapper(component *component, realmId } func (keycloakClient *KeycloakClient) NewLdapUserAttributeMapper(ldapUserAttributeMapper *LdapUserAttributeMapper) error { - location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/components", ldapUserAttributeMapper.RealmId), convertFromLdapUserAttributeMapperToComponent(ldapUserAttributeMapper)) + _, location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/components", ldapUserAttributeMapper.RealmId), convertFromLdapUserAttributeMapperToComponent(ldapUserAttributeMapper)) if err != nil { return err } @@ -89,7 +89,7 @@ func (keycloakClient *KeycloakClient) NewLdapUserAttributeMapper(ldapUserAttribu func (keycloakClient *KeycloakClient) GetLdapUserAttributeMapper(realmId, id string) (*LdapUserAttributeMapper, error) { var component *component - err := keycloakClient.get(fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component, nil) if err != nil { return nil, err } @@ -102,5 +102,5 @@ func (keycloakClient *KeycloakClient) UpdateLdapUserAttributeMapper(ldapUserAttr } func (keycloakClient *KeycloakClient) DeleteLdapUserAttributeMapper(realmId, id string) error { - return keycloakClient.delete(fmt.Sprintf("/realms/%s/components/%s", realmId, id)) + return keycloakClient.delete(fmt.Sprintf("/realms/%s/components/%s", realmId, id), nil) } diff --git a/keycloak/ldap_user_federation.go b/keycloak/ldap_user_federation.go index 4528b13e..ea1b3d05 100644 --- a/keycloak/ldap_user_federation.go +++ b/keycloak/ldap_user_federation.go @@ -303,7 +303,7 @@ func (keycloakClient *KeycloakClient) NewLdapUserFederation(ldapUserFederation * return err } - location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/components", ldapUserFederation.RealmId), component) + _, location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/components", ldapUserFederation.RealmId), component) if err != nil { return err } @@ -316,7 +316,7 @@ func (keycloakClient *KeycloakClient) NewLdapUserFederation(ldapUserFederation * func (keycloakClient *KeycloakClient) GetLdapUserFederation(realmId, id string) (*LdapUserFederation, error) { var component *component - err := keycloakClient.get(fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/components/%s", realmId, id), &component, nil) if err != nil { return nil, err } @@ -334,5 +334,5 @@ func (keycloakClient *KeycloakClient) UpdateLdapUserFederation(ldapUserFederatio } func (keycloakClient *KeycloakClient) DeleteLdapUserFederation(realmId, id string) error { - return keycloakClient.delete(fmt.Sprintf("/realms/%s/components/%s", realmId, id)) + return keycloakClient.delete(fmt.Sprintf("/realms/%s/components/%s", realmId, id), nil) } diff --git a/keycloak/openid_audience_protocol_mapper.go b/keycloak/openid_audience_protocol_mapper.go index 93eef654..3e204e57 100644 --- a/keycloak/openid_audience_protocol_mapper.go +++ b/keycloak/openid_audience_protocol_mapper.go @@ -63,7 +63,7 @@ func (protocolMapper *protocolMapper) convertToOpenIdAudienceProtocolMapper(real func (keycloakClient *KeycloakClient) GetOpenIdAudienceProtocolMapper(realmId, clientId, clientScopeId, mapperId string) (*OpenIdAudienceProtocolMapper, error) { var protocolMapper *protocolMapper - err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper) + err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper, nil) if err != nil { return nil, err } @@ -72,13 +72,13 @@ func (keycloakClient *KeycloakClient) GetOpenIdAudienceProtocolMapper(realmId, c } func (keycloakClient *KeycloakClient) DeleteOpenIdAudienceProtocolMapper(realmId, clientId, clientScopeId, mapperId string) error { - return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId)) + return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), nil) } func (keycloakClient *KeycloakClient) NewOpenIdAudienceProtocolMapper(mapper *OpenIdAudienceProtocolMapper) error { path := protocolMapperPath(mapper.RealmId, mapper.ClientId, mapper.ClientScopeId) - location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) + _, location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) if err != nil { return err } diff --git a/keycloak/openid_client.go b/keycloak/openid_client.go index e97985e6..9bf9ca22 100644 --- a/keycloak/openid_client.go +++ b/keycloak/openid_client.go @@ -4,34 +4,79 @@ import ( "fmt" ) -type openidClientSecret struct { +type OpenidClientRole struct { + Id string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + ScopeParamRequired bool `json:"scopeParamRequired"` + ClientRole bool `json:"clientRole"` + ContainerId string `json:"ContainerId"` +} + +type OpenidClientSecret struct { Type string `json:"type"` Value string `json:"value"` } -type OpenidClient struct { - Id string `json:"id,omitempty"` - ClientId string `json:"clientId"` - RealmId string `json:"-"` - Name string `json:"name"` - Protocol string `json:"protocol"` // always openid-connect for this resource - ClientAuthenticatorType string `json:"clientAuthenticatorType"` // always client-secret for now, don't have a need for JWT here - ClientSecret string `json:"secret,omitempty"` +type OpenidClientAuthorizationSettings struct { + PolicyEnforcementMode string `json:"policyEnforcementMode,omitempty"` + AllowRemoteResourceManagement bool `json:"allowRemoteResourceManagement,omitempty"` + KeepDefaults bool `json:"-"` +} - Enabled bool `json:"enabled"` - Description string `json:"description"` +type OpenidClient struct { + Id string `json:"id,omitempty"` + ClientId string `json:"clientId"` + RealmId string `json:"-"` + Name string `json:"name"` + Protocol string `json:"protocol"` // always openid-connect for this resource + ClientAuthenticatorType string `json:"clientAuthenticatorType"` // always client-secret for now, don't have a need for JWT here + ClientSecret string `json:"secret,omitempty"` + Enabled bool `json:"enabled"` + Description string `json:"description"` + PublicClient bool `json:"publicClient"` + BearerOnly bool `json:"bearerOnly"` + StandardFlowEnabled bool `json:"standardFlowEnabled"` + ImplicitFlowEnabled bool `json:"implicitFlowEnabled"` + DirectAccessGrantsEnabled bool `json:"directAccessGrantsEnabled"` + ServiceAccountsEnabled bool `json:"serviceAccountsEnabled"` + AuthorizationServicesEnabled bool `json:"authorizationServicesEnabled"` + ValidRedirectUris []string `json:"redirectUris"` + WebOrigins []string `json:"webOrigins"` + AuthorizationSettings *OpenidClientAuthorizationSettings `json:"authorizationSettings,omitempty"` +} - // Attributes below indicate client access type. If both are false, access type is confidential. Both cannot be true (although the Keycloak API lets you do this) - PublicClient bool `json:"publicClient"` - BearerOnly bool `json:"bearerOnly"` +func (keycloakClient *KeycloakClient) GetClientRoleByName(realm, clientId, name string) (*OpenidClientRole, error) { + var clientRole OpenidClientRole + err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s/roles/%s", realm, clientId, name), &clientRole, nil) + if err != nil { + return nil, err + } + return &clientRole, nil +} - StandardFlowEnabled bool `json:"standardFlowEnabled"` - ImplicitFlowEnabled bool `json:"implicitFlowEnabled"` - DirectAccessGrantsEnabled bool `json:"directAccessGrantsEnabled"` - ServiceAccountsEnabled bool `json:"serviceAccountsEnabled"` +func (keycloakClient *KeycloakClient) GetClientByName(realm, clientId string) (*OpenidClient, error) { + var clients []OpenidClient + params := map[string]string{"clientId": clientId} + err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients", realm), &clients, params) + if err != nil { + return nil, err + } + if len(clients) == 0 { + return nil, fmt.Errorf("no clients with name %s found", clientId) + } + client := clients[0] + client.RealmId = realm + return &client, nil +} - ValidRedirectUris []string `json:"redirectUris"` - WebOrigins []string `json:"webOrigins"` +func (keycloakClient *KeycloakClient) GetOpenidClientServiceAccountUserId(realmId, clientId string) (*User, error) { + var serviceAccountUser User + err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s/service-account-user", realmId, clientId), &serviceAccountUser, nil) + if err != nil { + return &serviceAccountUser, err + } + return &serviceAccountUser, nil } func (keycloakClient *KeycloakClient) ValidateOpenidClient(client *OpenidClient) error { @@ -54,26 +99,39 @@ func (keycloakClient *KeycloakClient) NewOpenidClient(client *OpenidClient) erro client.Protocol = "openid-connect" client.ClientAuthenticatorType = "client-secret" - location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/clients", client.RealmId), client) + _, location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/clients", client.RealmId), client) if err != nil { return err } client.Id = getIdFromLocationHeader(location) + if authorizationSettings := client.AuthorizationSettings; authorizationSettings != nil { + if !(*authorizationSettings).KeepDefaults { + resource, err := keycloakClient.GetOpenidClientAuthorizationResourceByName(client.RealmId, client.Id, "default") + if err != nil { + return err + } + err = keycloakClient.DeleteOpenidClientAuthorizationResource(resource.RealmId, resource.ResourceServerId, resource.Id) + if err != nil { + return err + } + } + } + return nil } func (keycloakClient *KeycloakClient) GetOpenidClient(realmId, id string) (*OpenidClient, error) { var client OpenidClient - var clientSecret openidClientSecret + var clientSecret OpenidClientSecret - err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s", realmId, id), &client) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s", realmId, id), &client, nil) if err != nil { return nil, err } - err = keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s/client-secret", realmId, id), &clientSecret) + err = keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s/client-secret", realmId, id), &clientSecret, nil) if err != nil { return nil, err } @@ -86,9 +144,13 @@ func (keycloakClient *KeycloakClient) GetOpenidClient(realmId, id string) (*Open func (keycloakClient *KeycloakClient) GetOpenidClientByClientId(realmId, clientId string) (*OpenidClient, error) { var clients []OpenidClient - var clientSecret openidClientSecret + var clientSecret OpenidClientSecret + + params := map[string]string{ + "clientId": clientId, + } - err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients?clientId=%s", realmId, clientId), &clients) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients", realmId), &clients, params) if err != nil { return nil, err } @@ -99,7 +161,7 @@ func (keycloakClient *KeycloakClient) GetOpenidClientByClientId(realmId, clientI client := clients[0] - err = keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s/client-secret", realmId, client.Id), &clientSecret) + err = keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s/client-secret", realmId, client.Id), &clientSecret, nil) if err != nil { return nil, err } @@ -118,13 +180,13 @@ func (keycloakClient *KeycloakClient) UpdateOpenidClient(client *OpenidClient) e } func (keycloakClient *KeycloakClient) DeleteOpenidClient(realmId, id string) error { - return keycloakClient.delete(fmt.Sprintf("/realms/%s/clients/%s", realmId, id)) + return keycloakClient.delete(fmt.Sprintf("/realms/%s/clients/%s", realmId, id), nil) } func (keycloakClient *KeycloakClient) getOpenidClientScopes(realmId, clientId, t string) ([]*OpenidClientScope, error) { var scopes []*OpenidClientScope - err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s/%s-client-scopes", realmId, clientId, t), &scopes) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s/%s-client-scopes", realmId, clientId, t), &scopes, nil) if err != nil { return nil, err } @@ -207,7 +269,7 @@ func (keycloakClient *KeycloakClient) detachOpenidClientScopes(realmId, clientId } for _, openidClientScope := range allOpenidClientScopes { - err := keycloakClient.delete(fmt.Sprintf("/realms/%s/clients/%s/%s-client-scopes/%s", realmId, clientId, t, openidClientScope.Id)) + err := keycloakClient.delete(fmt.Sprintf("/realms/%s/clients/%s/%s-client-scopes/%s", realmId, clientId, t, openidClientScope.Id), nil) if err != nil { return err } diff --git a/keycloak/openid_client_authorization_permission.go b/keycloak/openid_client_authorization_permission.go new file mode 100644 index 00000000..f8403b00 --- /dev/null +++ b/keycloak/openid_client_authorization_permission.go @@ -0,0 +1,78 @@ +package keycloak + +import ( + "encoding/json" + "fmt" +) + +type OpenidClientAuthorizationPermission struct { + Id string `json:"id,omitempty"` + RealmId string `json:"-"` + ResourceServerId string `json:"-"` + Name string `json:"name"` + Description string `json:"description"` + DecisionStrategy string `json:"decisionStrategy"` + Policies []string `json:"policies"` + Resources []string `json:"resources"` + Type string `json:"type"` +} + +func (keycloakClient *KeycloakClient) GetOpenidClientAuthorizationPermission(realm, resourceServerId, id string) (*OpenidClientAuthorizationPermission, error) { + permission := OpenidClientAuthorizationPermission{ + RealmId: realm, + ResourceServerId: resourceServerId, + Id: id, + } + + policies := []OpenidClientAuthorizationPolicy{} + resources := []OpenidClientAuthorizationResource{} + + err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/permission/resource/%s", realm, resourceServerId, id), &permission, nil) + if err != nil { + return nil, err + } + + err = keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/policy/%s/associatedPolicies", realm, resourceServerId, id), &policies, nil) + if err != nil { + return nil, err + } + + err = keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/permission/%s/resources", realm, resourceServerId, id), &resources, nil) + if err != nil { + return nil, err + } + + for _, policy := range policies { + permission.Policies = append(permission.Policies, policy.Id) + } + + for _, resource := range resources { + permission.Resources = append(permission.Resources, resource.Id) + } + + return &permission, nil +} + +func (keycloakClient *KeycloakClient) NewOpenidClientAuthorizationPermission(permission *OpenidClientAuthorizationPermission) error { + body, _, err := keycloakClient.post(fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/permission", permission.RealmId, permission.ResourceServerId), permission) + if err != nil { + return err + } + err = json.Unmarshal(body, &permission) + if err != nil { + return err + } + return nil +} + +func (keycloakClient *KeycloakClient) UpdateOpenidClientAuthorizationPermission(permission *OpenidClientAuthorizationPermission) error { + err := keycloakClient.put(fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/permission/resource/%s", permission.RealmId, permission.ResourceServerId, permission.Id), permission) + if err != nil { + return err + } + return nil +} + +func (keycloakClient *KeycloakClient) DeleteOpenidClientAuthorizationPermission(realmId, resourceServerId, permissionId string) error { + return keycloakClient.delete(fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/permission/%s", realmId, resourceServerId, permissionId), nil) +} diff --git a/keycloak/openid_client_authorization_policy.go b/keycloak/openid_client_authorization_policy.go new file mode 100644 index 00000000..214fbbe0 --- /dev/null +++ b/keycloak/openid_client_authorization_policy.go @@ -0,0 +1,33 @@ +package keycloak + +import ( + "fmt" +) + +type OpenidClientAuthorizationPolicy struct { + Id string `json:"id,omitempty"` + RealmId string `json:"-"` + ResourceServerId string `json:"-"` + Name string `json:"name"` + Owner string `json:"owner"` + DecisionStrategy string `json:"decisionStrategy"` + Logic string `json:"logic"` + Policies []string `json:"policies"` + Resources []string `json:"resources"` + Scopes []string `json:"scopes"` + Type string `json:"type"` +} + +func (keycloakClient *KeycloakClient) GetClientAuthorizationPolicyByName(realmId, resourceServerId, name string) (*OpenidClientAuthorizationPolicy, error) { + policies := []OpenidClientAuthorizationPolicy{} + params := map[string]string{"name": name} + err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/policy", realmId, resourceServerId), &policies, params) + if err != nil { + return nil, err + } + policy := policies[0] + policy.RealmId = realmId + policy.ResourceServerId = resourceServerId + policy.Name = name + return &policy, nil +} diff --git a/keycloak/openid_client_authorization_resource.go b/keycloak/openid_client_authorization_resource.go new file mode 100644 index 00000000..d1b0de6b --- /dev/null +++ b/keycloak/openid_client_authorization_resource.go @@ -0,0 +1,70 @@ +package keycloak + +import ( + "encoding/json" + "fmt" +) + +type OpenidClientAuthorizationResource struct { + ResourceServerId string `json:"-"` + RealmId string `json:"-"` + Id string `json:"_id,omitempty"` + DisplayName string `json:"displayName"` + Name string `json:"name"` + Uris []string `json:"uris"` + IconUri string `json:"icon_uri"` + OwnerManagedAccess bool `json:"ownerManagedAccess"` + Scopes []OpenidClientAuthorizationScope `json:"scopes"` + Type string `json:"type"` + Attributes map[string][]string `json:"attributes"` +} + +func (keycloakClient *KeycloakClient) NewOpenidClientAuthorizationResource(resource *OpenidClientAuthorizationResource) error { + body, _, err := keycloakClient.post(fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/resource", resource.RealmId, resource.ResourceServerId), resource) + if err != nil { + return err + } + err = json.Unmarshal(body, &resource) + if err != nil { + return err + } + return nil +} + +func (keycloakClient *KeycloakClient) GetOpenidClientAuthorizationResource(realm, resourceServerId, resourceId string) (*OpenidClientAuthorizationResource, error) { + resource := OpenidClientAuthorizationResource{ + RealmId: realm, + ResourceServerId: resourceServerId, + } + err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/resource/%s", realm, resourceServerId, resourceId), &resource, nil) + if err != nil { + return nil, err + } + return &resource, nil +} + +func (keycloakClient *KeycloakClient) GetOpenidClientAuthorizationResourceByName(realmId, resourceServerId, name string) (*OpenidClientAuthorizationResource, error) { + resources := []OpenidClientAuthorizationResource{} + params := map[string]string{"name": name} + err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/resource", realmId, resourceServerId), &resources, params) + if err != nil { + return nil, err + } + resource := resources[0] + resource.RealmId = realmId + resource.ResourceServerId = resourceServerId + resource.Name = name + return &resource, nil +} + +func (keycloakClient *KeycloakClient) UpdateOpenidClientAuthorizationResource(resource *OpenidClientAuthorizationResource) error { + err := keycloakClient.put(fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/resource/%s", resource.RealmId, resource.ResourceServerId, resource.Id), resource) + if err != nil { + return err + } + return nil +} + +func (keycloakClient *KeycloakClient) DeleteOpenidClientAuthorizationResource(realmId, clientId, resourceId string) error { + return keycloakClient.delete(fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/resource/%s", realmId, clientId, resourceId), nil) +} diff --git a/keycloak/openid_client_authorization_scope.go b/keycloak/openid_client_authorization_scope.go new file mode 100644 index 00000000..2490f70e --- /dev/null +++ b/keycloak/openid_client_authorization_scope.go @@ -0,0 +1,51 @@ +package keycloak + +import ( + "encoding/json" + "fmt" +) + +type OpenidClientAuthorizationScope struct { + Id string `json:"id,omitempty"` + RealmId string `json:"-"` + ResourceServerId string `json:"-"` + Name string `json:"name"` + DisplayName string `json:"displayName"` + IconUri string `json:"iconUri"` +} + +func (keycloakClient *KeycloakClient) NewOpenidClientAuthorizationScope(scope *OpenidClientAuthorizationScope) error { + body, _, err := keycloakClient.post(fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/scope", scope.RealmId, scope.ResourceServerId), scope) + if err != nil { + return err + } + err = json.Unmarshal(body, &scope) + if err != nil { + return err + } + return nil +} + +func (keycloakClient *KeycloakClient) GetOpenidClientAuthorizationScope(realm, resourceServerId, scopeId string) (*OpenidClientAuthorizationScope, error) { + scope := OpenidClientAuthorizationScope{ + RealmId: realm, + ResourceServerId: resourceServerId, + } + err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/scope/%s", realm, resourceServerId, scopeId), &scope, nil) + if err != nil { + return nil, err + } + return &scope, nil +} + +func (keycloakClient *KeycloakClient) UpdateOpenidClientAuthorizationScope(scope *OpenidClientAuthorizationScope) error { + err := keycloakClient.put(fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/scope/%s", scope.RealmId, scope.ResourceServerId, scope.Id), scope) + if err != nil { + return err + } + return nil +} + +func (keycloakClient *KeycloakClient) DeleteOpenidClientAuthorizationScope(realmId, resourceServerId, scopeId string) error { + return keycloakClient.delete(fmt.Sprintf("/realms/%s/clients/%s/authz/resource-server/scope/%s", realmId, resourceServerId, scopeId), nil) +} diff --git a/keycloak/openid_client_scope.go b/keycloak/openid_client_scope.go index 13d9b462..c21aba49 100644 --- a/keycloak/openid_client_scope.go +++ b/keycloak/openid_client_scope.go @@ -21,7 +21,7 @@ type OpenidClientScopeFilterFunc func(*OpenidClientScope) bool func (keycloakClient *KeycloakClient) NewOpenidClientScope(clientScope *OpenidClientScope) error { clientScope.Protocol = "openid-connect" - location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/client-scopes", clientScope.RealmId), clientScope) + _, location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/client-scopes", clientScope.RealmId), clientScope) if err != nil { return err } @@ -34,7 +34,7 @@ func (keycloakClient *KeycloakClient) NewOpenidClientScope(clientScope *OpenidCl func (keycloakClient *KeycloakClient) GetOpenidClientScope(realmId, id string) (*OpenidClientScope, error) { var clientScope OpenidClientScope - err := keycloakClient.get(fmt.Sprintf("/realms/%s/client-scopes/%s", realmId, id), &clientScope) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/client-scopes/%s", realmId, id), &clientScope, nil) if err != nil { return nil, err } @@ -51,14 +51,14 @@ func (keycloakClient *KeycloakClient) UpdateOpenidClientScope(clientScope *Openi } func (keycloakClient *KeycloakClient) DeleteOpenidClientScope(realmId, id string) error { - return keycloakClient.delete(fmt.Sprintf("/realms/%s/client-scopes/%s", realmId, id)) + return keycloakClient.delete(fmt.Sprintf("/realms/%s/client-scopes/%s", realmId, id), nil) } func (keycloakClient *KeycloakClient) listOpenidClientScopesWithFilter(realmId string, filter OpenidClientScopeFilterFunc) ([]*OpenidClientScope, error) { var clientScopes []OpenidClientScope var openidClientScopes []*OpenidClientScope - err := keycloakClient.get(fmt.Sprintf("/realms/%s/client-scopes", realmId), &clientScopes) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/client-scopes", realmId), &clientScopes, nil) if err != nil { return nil, err } diff --git a/keycloak/openid_client_service_account_role.go b/keycloak/openid_client_service_account_role.go new file mode 100644 index 00000000..d8e6aa89 --- /dev/null +++ b/keycloak/openid_client_service_account_role.go @@ -0,0 +1,64 @@ +package keycloak + +import ( + "fmt" +) + +type OpenidClientServiceAccountRole struct { + Id string `json:"id"` + RealmId string `json:"-"` + ServiceAccountUserId string `json:"-"` + Name string `json:"name,omitempty"` + ClientRole bool `json:"clientRole"` + Composite bool `json:"composite"` + ContainerId string `json:"containerId"` + Description string `json:"description"` +} + +func (keycloakClient *KeycloakClient) NewOpenidClientServiceAccountRole(serviceAccountRole *OpenidClientServiceAccountRole) error { + role, err := keycloakClient.GetClientRoleByName(serviceAccountRole.RealmId, serviceAccountRole.ContainerId, serviceAccountRole.Name) + if err != nil { + return err + } + serviceAccountRole.Id = role.Id + serviceAccountRoles := []OpenidClientServiceAccountRole{*serviceAccountRole} + _, _, err = keycloakClient.post(fmt.Sprintf("/realms/%s/users/%s/role-mappings/clients/%s", serviceAccountRole.RealmId, serviceAccountRole.ServiceAccountUserId, serviceAccountRole.ContainerId), serviceAccountRoles) + if err != nil { + return err + } + return nil +} + +func (keycloakClient *KeycloakClient) DeleteOpenidClientServiceAccountRole(realm, serviceAccountUserId, clientId, roleId string) error { + serviceAccountRole, err := keycloakClient.GetOpenidClientServiceAccountRole(realm, serviceAccountUserId, clientId, roleId) + if err != nil { + return err + } + serviceAccountRoles := []OpenidClientServiceAccountRole{*serviceAccountRole} + err = keycloakClient.delete(fmt.Sprintf("/realms/%s/users/%s/role-mappings/clients/%s", realm, serviceAccountUserId, clientId), &serviceAccountRoles) + if err != nil { + return err + } + return nil +} + +func (keycloakClient *KeycloakClient) GetOpenidClientServiceAccountRole(realm, serviceAccountUserId, clientId, roleId string) (*OpenidClientServiceAccountRole, error) { + serviceAccountRoles := []OpenidClientServiceAccountRole{ + { + Id: roleId, + RealmId: realm, + ContainerId: clientId, + ServiceAccountUserId: serviceAccountUserId, + }, + } + err := keycloakClient.get(fmt.Sprintf("/realms/%s/users/%s/role-mappings/clients/%s", realm, serviceAccountUserId, clientId), &serviceAccountRoles, nil) + if err != nil { + return nil, err + } + for _, serviceAccountRole := range serviceAccountRoles { + if serviceAccountRole.Id == roleId { + return &serviceAccountRole, nil + } + } + return &OpenidClientServiceAccountRole{}, nil +} diff --git a/keycloak/openid_full_name_protocol_mapper.go b/keycloak/openid_full_name_protocol_mapper.go index fa6c7f06..a77b82ae 100644 --- a/keycloak/openid_full_name_protocol_mapper.go +++ b/keycloak/openid_full_name_protocol_mapper.go @@ -63,7 +63,7 @@ func (protocolMapper *protocolMapper) convertToOpenIdFullNameProtocolMapper(real func (keycloakClient *KeycloakClient) GetOpenIdFullNameProtocolMapper(realmId, clientId, clientScopeId, mapperId string) (*OpenIdFullNameProtocolMapper, error) { var protocolMapper *protocolMapper - err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper) + err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper, nil) if err != nil { return nil, err } @@ -72,13 +72,13 @@ func (keycloakClient *KeycloakClient) GetOpenIdFullNameProtocolMapper(realmId, c } func (keycloakClient *KeycloakClient) DeleteOpenIdFullNameProtocolMapper(realmId, clientId, clientScopeId, mapperId string) error { - return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId)) + return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), nil) } func (keycloakClient *KeycloakClient) NewOpenIdFullNameProtocolMapper(mapper *OpenIdFullNameProtocolMapper) error { path := protocolMapperPath(mapper.RealmId, mapper.ClientId, mapper.ClientScopeId) - location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) + _, location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) if err != nil { return err } diff --git a/keycloak/openid_group_membership_protocol_mapper.go b/keycloak/openid_group_membership_protocol_mapper.go index f5a0e900..256c2977 100644 --- a/keycloak/openid_group_membership_protocol_mapper.go +++ b/keycloak/openid_group_membership_protocol_mapper.go @@ -75,7 +75,7 @@ func (protocolMapper *protocolMapper) convertToOpenIdGroupMembershipProtocolMapp func (keycloakClient *KeycloakClient) GetOpenIdGroupMembershipProtocolMapper(realmId, clientId, clientScopeId, mapperId string) (*OpenIdGroupMembershipProtocolMapper, error) { var protocolMapper *protocolMapper - err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper) + err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper, nil) if err != nil { return nil, err } @@ -84,13 +84,13 @@ func (keycloakClient *KeycloakClient) GetOpenIdGroupMembershipProtocolMapper(rea } func (keycloakClient *KeycloakClient) DeleteOpenIdGroupMembershipProtocolMapper(realmId, clientId, clientScopeId, mapperId string) error { - return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId)) + return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), nil) } func (keycloakClient *KeycloakClient) NewOpenIdGroupMembershipProtocolMapper(mapper *OpenIdGroupMembershipProtocolMapper) error { path := protocolMapperPath(mapper.RealmId, mapper.ClientId, mapper.ClientScopeId) - location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) + _, location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) if err != nil { return err } diff --git a/keycloak/openid_hardcoded_claim_protocol_mapper.go b/keycloak/openid_hardcoded_claim_protocol_mapper.go index 31f879f1..7a921a1c 100644 --- a/keycloak/openid_hardcoded_claim_protocol_mapper.go +++ b/keycloak/openid_hardcoded_claim_protocol_mapper.go @@ -74,7 +74,7 @@ func (protocolMapper *protocolMapper) convertToOpenIdHardcodedClaimProtocolMappe func (keycloakClient *KeycloakClient) GetOpenIdHardcodedClaimProtocolMapper(realmId, clientId, clientScopeId, mapperId string) (*OpenIdHardcodedClaimProtocolMapper, error) { var protocolMapper *protocolMapper - err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper) + err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper, nil) if err != nil { return nil, err } @@ -83,13 +83,13 @@ func (keycloakClient *KeycloakClient) GetOpenIdHardcodedClaimProtocolMapper(real } func (keycloakClient *KeycloakClient) DeleteOpenIdHardcodedClaimProtocolMapper(realmId, clientId, clientScopeId, mapperId string) error { - return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId)) + return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), nil) } func (keycloakClient *KeycloakClient) NewOpenIdHardcodedClaimProtocolMapper(mapper *OpenIdHardcodedClaimProtocolMapper) error { path := protocolMapperPath(mapper.RealmId, mapper.ClientId, mapper.ClientScopeId) - location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) + _, location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) if err != nil { return err } diff --git a/keycloak/openid_user_attribute_protocol_mapper.go b/keycloak/openid_user_attribute_protocol_mapper.go index 4615a996..ea9faf3a 100644 --- a/keycloak/openid_user_attribute_protocol_mapper.go +++ b/keycloak/openid_user_attribute_protocol_mapper.go @@ -84,7 +84,7 @@ func (protocolMapper *protocolMapper) convertToOpenIdUserAttributeProtocolMapper func (keycloakClient *KeycloakClient) GetOpenIdUserAttributeProtocolMapper(realmId, clientId, clientScopeId, mapperId string) (*OpenIdUserAttributeProtocolMapper, error) { var protocolMapper *protocolMapper - err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper) + err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper, nil) if err != nil { return nil, err } @@ -93,13 +93,13 @@ func (keycloakClient *KeycloakClient) GetOpenIdUserAttributeProtocolMapper(realm } func (keycloakClient *KeycloakClient) DeleteOpenIdUserAttributeProtocolMapper(realmId, clientId, clientScopeId, mapperId string) error { - return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId)) + return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), nil) } func (keycloakClient *KeycloakClient) NewOpenIdUserAttributeProtocolMapper(mapper *OpenIdUserAttributeProtocolMapper) error { path := protocolMapperPath(mapper.RealmId, mapper.ClientId, mapper.ClientScopeId) - location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) + _, location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) if err != nil { return err } diff --git a/keycloak/openid_user_property_protocol_mapper.go b/keycloak/openid_user_property_protocol_mapper.go index 522aaa66..9595bced 100644 --- a/keycloak/openid_user_property_protocol_mapper.go +++ b/keycloak/openid_user_property_protocol_mapper.go @@ -74,7 +74,7 @@ func (protocolMapper *protocolMapper) convertToOpenIdUserPropertyProtocolMapper( func (keycloakClient *KeycloakClient) GetOpenIdUserPropertyProtocolMapper(realmId, clientId, clientScopeId, mapperId string) (*OpenIdUserPropertyProtocolMapper, error) { var protocolMapper *protocolMapper - err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper) + err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper, nil) if err != nil { return nil, err } @@ -83,13 +83,13 @@ func (keycloakClient *KeycloakClient) GetOpenIdUserPropertyProtocolMapper(realmI } func (keycloakClient *KeycloakClient) DeleteOpenIdUserPropertyProtocolMapper(realmId, clientId, clientScopeId, mapperId string) error { - return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId)) + return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), nil) } func (keycloakClient *KeycloakClient) NewOpenIdUserPropertyProtocolMapper(mapper *OpenIdUserPropertyProtocolMapper) error { path := protocolMapperPath(mapper.RealmId, mapper.ClientId, mapper.ClientScopeId) - location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) + _, location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) if err != nil { return err } diff --git a/keycloak/protocol_mapper.go b/keycloak/protocol_mapper.go index 51465472..92693e1c 100644 --- a/keycloak/protocol_mapper.go +++ b/keycloak/protocol_mapper.go @@ -48,7 +48,7 @@ func individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId str func (keycloakClient *KeycloakClient) listGenericProtocolMappers(realmId, clientId, clientScopeId string) ([]*protocolMapper, error) { var protocolMappers []*protocolMapper - err := keycloakClient.get(protocolMapperPath(realmId, clientId, clientScopeId), &protocolMappers) + err := keycloakClient.get(protocolMapperPath(realmId, clientId, clientScopeId), &protocolMappers, nil) if err != nil { return nil, err } diff --git a/keycloak/realm.go b/keycloak/realm.go index 3671d8a1..d45a2f62 100644 --- a/keycloak/realm.go +++ b/keycloak/realm.go @@ -43,7 +43,7 @@ type Realm struct { } func (keycloakClient *KeycloakClient) NewRealm(realm *Realm) error { - _, err := keycloakClient.post("/realms", realm) + _, _, err := keycloakClient.post("/realms", realm) return err } @@ -51,7 +51,7 @@ func (keycloakClient *KeycloakClient) NewRealm(realm *Realm) error { func (keycloakClient *KeycloakClient) GetRealm(id string) (*Realm, error) { var realm Realm - err := keycloakClient.get(fmt.Sprintf("/realms/%s", id), &realm) + err := keycloakClient.get(fmt.Sprintf("/realms/%s", id), &realm, nil) if err != nil { return nil, err } @@ -64,10 +64,10 @@ func (keycloakClient *KeycloakClient) UpdateRealm(realm *Realm) error { } func (keycloakClient *KeycloakClient) DeleteRealm(id string) error { - err := keycloakClient.delete(fmt.Sprintf("/realms/%s", id)) + err := keycloakClient.delete(fmt.Sprintf("/realms/%s", id), nil) if err != nil { // For whatever reason, this fails sometimes with a 500 during acceptance tests. try again - return keycloakClient.delete(fmt.Sprintf("/realms/%s", id)) + return keycloakClient.delete(fmt.Sprintf("/realms/%s", id), nil) } return nil diff --git a/keycloak/saml_client.go b/keycloak/saml_client.go index be060a12..7350bb01 100644 --- a/keycloak/saml_client.go +++ b/keycloak/saml_client.go @@ -47,7 +47,7 @@ func (keycloakClient *KeycloakClient) NewSamlClient(client *SamlClient) error { client.Protocol = "saml" client.ClientAuthenticatorType = "client-secret" - location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/clients", client.RealmId), client) + _, location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/clients", client.RealmId), client) if err != nil { return err } @@ -60,7 +60,7 @@ func (keycloakClient *KeycloakClient) NewSamlClient(client *SamlClient) error { func (keycloakClient *KeycloakClient) GetSamlClient(realmId, id string) (*SamlClient, error) { var client SamlClient - err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s", realmId, id), &client) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/clients/%s", realmId, id), &client, nil) if err != nil { return nil, err } @@ -78,5 +78,5 @@ func (keycloakClient *KeycloakClient) UpdateSamlClient(client *SamlClient) error } func (keycloakClient *KeycloakClient) DeleteSamlClient(realmId, id string) error { - return keycloakClient.delete(fmt.Sprintf("/realms/%s/clients/%s", realmId, id)) + return keycloakClient.delete(fmt.Sprintf("/realms/%s/clients/%s", realmId, id), nil) } diff --git a/keycloak/saml_user_attribute_protocol_mapper.go b/keycloak/saml_user_attribute_protocol_mapper.go index 0cbee86d..5c760fdd 100644 --- a/keycloak/saml_user_attribute_protocol_mapper.go +++ b/keycloak/saml_user_attribute_protocol_mapper.go @@ -50,7 +50,7 @@ func (protocolMapper *protocolMapper) convertToSamlUserAttributeProtocolMapper(r func (keycloakClient *KeycloakClient) GetSamlUserAttributeProtocolMapper(realmId, clientId, clientScopeId, mapperId string) (*SamlUserAttributeProtocolMapper, error) { var protocolMapper *protocolMapper - err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper) + err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper, nil) if err != nil { return nil, err } @@ -59,13 +59,13 @@ func (keycloakClient *KeycloakClient) GetSamlUserAttributeProtocolMapper(realmId } func (keycloakClient *KeycloakClient) DeleteSamlUserAttributeProtocolMapper(realmId, clientId, clientScopeId, mapperId string) error { - return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId)) + return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), nil) } func (keycloakClient *KeycloakClient) NewSamlUserAttributeProtocolMapper(mapper *SamlUserAttributeProtocolMapper) error { path := protocolMapperPath(mapper.RealmId, mapper.ClientId, mapper.ClientScopeId) - location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) + _, location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) if err != nil { return err } diff --git a/keycloak/saml_user_property_protocol_mapper.go b/keycloak/saml_user_property_protocol_mapper.go index 194fedea..3024ca8a 100644 --- a/keycloak/saml_user_property_protocol_mapper.go +++ b/keycloak/saml_user_property_protocol_mapper.go @@ -50,7 +50,7 @@ func (protocolMapper *protocolMapper) convertToSamlUserPropertyProtocolMapper(re func (keycloakClient *KeycloakClient) GetSamlUserPropertyProtocolMapper(realmId, clientId, clientScopeId, mapperId string) (*SamlUserPropertyProtocolMapper, error) { var protocolMapper *protocolMapper - err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper) + err := keycloakClient.get(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), &protocolMapper, nil) if err != nil { return nil, err } @@ -59,13 +59,13 @@ func (keycloakClient *KeycloakClient) GetSamlUserPropertyProtocolMapper(realmId, } func (keycloakClient *KeycloakClient) DeleteSamlUserPropertyProtocolMapper(realmId, clientId, clientScopeId, mapperId string) error { - return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId)) + return keycloakClient.delete(individualProtocolMapperPath(realmId, clientId, clientScopeId, mapperId), nil) } func (keycloakClient *KeycloakClient) NewSamlUserPropertyProtocolMapper(mapper *SamlUserPropertyProtocolMapper) error { path := protocolMapperPath(mapper.RealmId, mapper.ClientId, mapper.ClientScopeId) - location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) + _, location, err := keycloakClient.post(path, mapper.convertToGenericProtocolMapper()) if err != nil { return err } diff --git a/keycloak/server_info.go b/keycloak/server_info.go index b8a2a5c0..48b7533f 100644 --- a/keycloak/server_info.go +++ b/keycloak/server_info.go @@ -41,7 +41,7 @@ func (serverInfo *ServerInfo) ComponentTypeIsInstalled(componentType, componentT func (keycloakClient *KeycloakClient) GetServerInfo() (*ServerInfo, error) { var serverInfo ServerInfo - err := keycloakClient.get("/serverinfo", &serverInfo) + err := keycloakClient.get("/serverinfo", &serverInfo, nil) if err != nil { return nil, err } diff --git a/keycloak/user.go b/keycloak/user.go index 4c36b3ed..8dbd9ce7 100644 --- a/keycloak/user.go +++ b/keycloak/user.go @@ -2,18 +2,27 @@ package keycloak import ( "fmt" - "net/url" ) +type FederatedIdentity struct { + IdentityProvider string `json:"identityProvider"` + UserId string `json:"userId"` + UserName string `json:"userName"` +} + +type FederatedIdentities []FederatedIdentity + type User struct { Id string `json:"id,omitempty"` RealmId string `json:"-"` - Username string `json:"username"` - Email string `json:"email"` - FirstName string `json:"firstName"` - LastName string `json:"lastName"` - Enabled bool `json:"enabled"` + Username string `json:"username"` + Email string `json:"email"` + FirstName string `json:"firstName"` + LastName string `json:"lastName"` + Enabled bool `json:"enabled"` + Attributes map[string][]string `json:"attributes"` + FederatedIdentities FederatedIdentities `json:"federatedIdentities"` } type PasswordCredentials struct { @@ -23,7 +32,7 @@ type PasswordCredentials struct { } func (keycloakClient *KeycloakClient) NewUser(user *User) error { - location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/users", user.RealmId), user) + _, location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/users", user.RealmId), user) if err != nil { return err } @@ -50,7 +59,7 @@ func (keycloakClient *KeycloakClient) ResetUserPassword(realmId, userId string, func (keycloakClient *KeycloakClient) GetUser(realmId, id string) (*User, error) { var user User - err := keycloakClient.get(fmt.Sprintf("/realms/%s/users/%s", realmId, id), &user) + err := keycloakClient.get(fmt.Sprintf("/realms/%s/users/%s", realmId, id), &user, nil) if err != nil { return nil, err } @@ -65,13 +74,17 @@ func (keycloakClient *KeycloakClient) UpdateUser(user *User) error { } func (keycloakClient *KeycloakClient) DeleteUser(realmId, id string) error { - return keycloakClient.delete(fmt.Sprintf("/realms/%s/users/%s", realmId, id)) + return keycloakClient.delete(fmt.Sprintf("/realms/%s/users/%s", realmId, id), nil) } func (keycloakClient *KeycloakClient) GetUserByUsername(realmId, username string) (*User, error) { var users []*User - err := keycloakClient.get(fmt.Sprintf("/realms/%s/users?username=%s", realmId, url.QueryEscape(username)), &users) + params := map[string]string{ + "username": username, + } + + err := keycloakClient.get(fmt.Sprintf("/realms/%s/users", realmId), &users, params) if err != nil { return nil, err } @@ -115,7 +128,7 @@ func (keycloakClient *KeycloakClient) AddUsersToGroup(realmId, groupId string, u } func (keycloakClient *KeycloakClient) RemoveUserFromGroup(user *User, groupId string) error { - return keycloakClient.delete(fmt.Sprintf("/realms/%s/users/%s/groups/%s", user.RealmId, user.Id, groupId)) + return keycloakClient.delete(fmt.Sprintf("/realms/%s/users/%s/groups/%s", user.RealmId, user.Id, groupId), nil) } func (keycloakClient *KeycloakClient) RemoveUsersFromGroup(realmId, groupId string, usernames []interface{}) error { diff --git a/provider/data_source_keycloak_openid_client.go b/provider/data_source_keycloak_openid_client.go new file mode 100644 index 00000000..8450c4b2 --- /dev/null +++ b/provider/data_source_keycloak_openid_client.go @@ -0,0 +1,116 @@ +package provider + +import ( + "github.com/hashicorp/terraform/helper/schema" + "github.com/mrparkers/terraform-provider-keycloak/keycloak" +) + +func dataSourceKeycloakOpenidClient() *schema.Resource { + return &schema.Resource{ + Read: dataSourceKeycloakOpenidClientRead, + + Schema: map[string]*schema.Schema{ + "client_id": { + Type: schema.TypeString, + Required: true, + }, + "realm_id": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "access_type": { + Type: schema.TypeString, + Computed: true, + }, + "client_secret": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + }, + "standard_flow_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "implicit_flow_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "direct_access_grants_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "valid_redirect_uris": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + Computed: true, + }, + "web_origins": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + Computed: true, + }, + "service_account_user_id": { + Type: schema.TypeString, + Computed: true, + }, + "service_accounts_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "resource_server_id": { + Type: schema.TypeString, + Computed: true, + }, + "authorization": { + Type: schema.TypeSet, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "policy_enforcement_mode": { + Type: schema.TypeString, + Computed: true, + }, + "allow_remote_resource_management": { + Type: schema.TypeBool, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceKeycloakOpenidClientRead(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + realmId := data.Get("realm_id").(string) + clientId := data.Get("client_id").(string) + + client, err := keycloakClient.GetClientByName(realmId, clientId) + if err != nil { + return handleNotFoundError(err, data) + } + + err = setOpenidClientData(keycloakClient, data, client) + if err != nil { + return err + } + + return nil +} diff --git a/provider/data_source_keycloak_openid_client_authorization_policy.go b/provider/data_source_keycloak_openid_client_authorization_policy.go new file mode 100644 index 00000000..4fb0622f --- /dev/null +++ b/provider/data_source_keycloak_openid_client_authorization_policy.go @@ -0,0 +1,90 @@ +package provider + +import ( + "github.com/hashicorp/terraform/helper/schema" + "github.com/mrparkers/terraform-provider-keycloak/keycloak" +) + +func dataSourceKeycloakOpenidClientAuthorizationPolicy() *schema.Resource { + return &schema.Resource{ + Read: dataSourceKeycloakOpenidClientAuthorizationPolicyRead, + + Schema: map[string]*schema.Schema{ + "resource_server_id": { + Type: schema.TypeString, + Required: true, + }, + "realm_id": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "decision_strategy": { + Type: schema.TypeString, + Computed: true, + }, + "owner": { + Type: schema.TypeString, + Computed: true, + }, + "logic": { + Type: schema.TypeString, + Computed: true, + }, + "policies": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + }, + "resources": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + }, + "scopes": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func setOpenidClientAuthorizationPolicyData(data *schema.ResourceData, policy *keycloak.OpenidClientAuthorizationPolicy) { + data.SetId(policy.Id) + + data.Set("resource_server_id", policy.ResourceServerId) + data.Set("realm_id", policy.RealmId) + data.Set("name", policy.Name) + data.Set("decision_strategy", policy.DecisionStrategy) + data.Set("owner", policy.Owner) + data.Set("logic", policy.Logic) + data.Set("policies", policy.Policies) + data.Set("resources", policy.Resources) + data.Set("scopes", policy.Scopes) + data.Set("type", policy.Type) +} + +func dataSourceKeycloakOpenidClientAuthorizationPolicyRead(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + realmId := data.Get("realm_id").(string) + resourceServerId := data.Get("resource_server_id").(string) + name := data.Get("name").(string) + + client, err := keycloakClient.GetClientAuthorizationPolicyByName(realmId, resourceServerId, name) + if err != nil { + return handleNotFoundError(err, data) + } + + setOpenidClientAuthorizationPolicyData(data, client) + + return nil +} diff --git a/provider/data_source_keycloak_openid_client_authorization_policy_test.go b/provider/data_source_keycloak_openid_client_authorization_policy_test.go new file mode 100644 index 00000000..8016a98e --- /dev/null +++ b/provider/data_source_keycloak_openid_client_authorization_policy_test.go @@ -0,0 +1,68 @@ +package provider + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "regexp" + "testing" +) + +func TestAccKeycloakDataSourceOpenidClientAuthorizationPolicy_basic(t *testing.T) { + realm := acctest.RandomWithPrefix("tf-acc-test") + clientId := acctest.RandomWithPrefix("tf-acc-test") + dataSourceName := "data.keycloak_openid_client_authorization_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccKeycloakOpenidClientAuthorizationPolicyConfig(realm, clientId), + Check: resource.ComposeTestCheckFunc( + resource.TestMatchResourceAttr(dataSourceName, "resource_server_id", regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$")), + resource.TestCheckResourceAttr(dataSourceName, "realm_id", realm), + resource.TestCheckResourceAttr(dataSourceName, "name", "default"), + resource.TestCheckResourceAttr(dataSourceName, "decision_strategy", "UNANIMOUS"), + resource.TestCheckResourceAttr(dataSourceName, "logic", "POSITIVE"), + resource.TestCheckResourceAttr(dataSourceName, "type", "resource"), + ), + }, + }, + }) +} + +func testAccKeycloakOpenidClientAuthorizationPolicyConfig(realm, clientId string) string { + return fmt.Sprintf(` +resource keycloak_realm test { + realm = "%s" + enabled = true + display_name = "foo" + account_theme = "base" + access_code_lifespan = "30m" +} + +resource keycloak_openid_client test { + client_id = "%s" + name = "%s" + realm_id = "${keycloak_realm.test.id}" + description = "a test openid client" + standard_flow_enabled = true + service_accounts_enabled = true + access_type = "CONFIDENTIAL" + client_secret = "secret" + valid_redirect_uris = [ + "http://localhost:5555/callback", + ] + authorization { + policy_enforcement_mode = "ENFORCING" + } +} + +data keycloak_openid_client_authorization_policy test { + resource_server_id = "${keycloak_openid_client.test.resource_server_id}" + realm_id = "${keycloak_realm.test.id}" + name = "default" +} +`, realm, clientId, clientId) +} diff --git a/provider/data_source_keycloak_openid_client_test.go b/provider/data_source_keycloak_openid_client_test.go new file mode 100644 index 00000000..b8408e18 --- /dev/null +++ b/provider/data_source_keycloak_openid_client_test.go @@ -0,0 +1,76 @@ +package provider + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "testing" +) + +func TestAccKeycloakDataSourceOpenidClient_basic(t *testing.T) { + realm := acctest.RandomWithPrefix("tf-acc-test") + clientId := acctest.RandomWithPrefix("tf-acc-test") + dataSourceName := "data.keycloak_openid_client.test" + resourceName := "keycloak_openid_client.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccKeycloakOpenidClientConfig(realm, clientId), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPair(dataSourceName, "client_id", resourceName, "client_id"), + resource.TestCheckResourceAttrPair(dataSourceName, "realm_id", resourceName, "realm_id"), + resource.TestCheckResourceAttrPair(dataSourceName, "name", resourceName, "name"), + resource.TestCheckResourceAttrPair(dataSourceName, "enabled", resourceName, "enabled"), + resource.TestCheckResourceAttrPair(dataSourceName, "description", resourceName, "description"), + resource.TestCheckResourceAttrPair(dataSourceName, "access_type", resourceName, "access_type"), + resource.TestCheckResourceAttrPair(dataSourceName, "standard_flow_enabled", resourceName, "standard_flow_enabled"), + resource.TestCheckResourceAttrPair(dataSourceName, "implicit_flow_enabled", resourceName, "implicit_flow_enabled"), + resource.TestCheckResourceAttrPair(dataSourceName, "direct_access_grants_enabled", resourceName, "direct_access_grants_enabled"), + resource.TestCheckResourceAttrPair(dataSourceName, "service_account_user_id", resourceName, "service_account_user_id"), + resource.TestCheckResourceAttrPair(dataSourceName, "service_accounts_enabled", resourceName, "service_accounts_enabled"), + resource.TestCheckResourceAttrPair(dataSourceName, "resource_server_id", resourceName, "resource_server_id"), + ), + }, + }, + }) +} + +func testAccKeycloakOpenidClientConfig(realm, clientId string) string { + return fmt.Sprintf(` +resource keycloak_realm test { + realm = "%s" + enabled = true + display_name = "foo" + account_theme = "base" + access_code_lifespan = "30m" +} + +resource keycloak_openid_client test { + name = "%s" + client_id = "%s" + realm_id = "${keycloak_realm.test.id}" + description = "a test openid client" + standard_flow_enabled = true + access_type = "CONFIDENTIAL" + service_accounts_enabled = true + client_secret = "secret" + valid_redirect_uris = [ + "http://localhost:5555/callback", + ] + authorization { + policy_enforcement_mode = "ENFORCING" + } + web_origins = [ + "http://localhost" + ] +} + +data keycloak_openid_client test { + client_id = "${keycloak_openid_client.test.client_id}" + realm_id = "${keycloak_realm.test.id}" +} +`, realm, clientId, clientId) +} diff --git a/provider/provider.go b/provider/provider.go index f5b0be72..2b3c28b9 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -7,6 +7,10 @@ import ( func KeycloakProvider() *schema.Provider { return &schema.Provider{ + DataSourcesMap: map[string]*schema.Resource{ + "keycloak_openid_client": dataSourceKeycloakOpenidClient(), + "keycloak_openid_client_authorization_policy": dataSourceKeycloakOpenidClientAuthorizationPolicy(), + }, ResourcesMap: map[string]*schema.Resource{ "keycloak_realm": resourceKeycloakRealm(), "keycloak_group": resourceKeycloakGroup(), @@ -38,6 +42,10 @@ func KeycloakProvider() *schema.Provider { "keycloak_user_template_importer_identity_provider_mapper": resourceKeycloakUserTemplateImporterIdentityProviderMapper(), "keycloak_saml_identity_provider": resourceKeycloakSamlIdentityProvider(), "keycloak_oidc_identity_provider": resourceKeycloakOidcIdentityProvider(), + "keycloak_openid_client_authorization_resource": resourceKeycloakOpenidClientAuthorizationResource(), + "keycloak_openid_client_authorization_scope": resourceKeycloakOpenidClientAuthorizationScope(), + "keycloak_openid_client_authorization_permission": resourceKeycloakOpenidClientAuthorizationPermission(), + "keycloak_openid_client_service_account_role": resourceKeycloakOpenidClientServiceAccountRole(), }, Schema: map[string]*schema.Schema{ "client_id": { diff --git a/provider/resource_keycloak_openid_client.go b/provider/resource_keycloak_openid_client.go index 66fb7f82..4d07112c 100644 --- a/provider/resource_keycloak_openid_client.go +++ b/provider/resource_keycloak_openid_client.go @@ -1,6 +1,7 @@ package provider import ( + "errors" "fmt" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" @@ -9,7 +10,8 @@ import ( ) var ( - keycloakOpenidClientAccessTypes = []string{"CONFIDENTIAL", "PUBLIC", "BEARER-ONLY"} + keycloakOpenidClientAccessTypes = []string{"CONFIDENTIAL", "PUBLIC", "BEARER-ONLY"} + keycloakOpenidClientAuthorizationPolicyEnforcementMode = []string{"ENFORCING", "PERMISSIVE", "DISABLED"} ) func resourceKeycloakOpenidClient() *schema.Resource { @@ -71,11 +73,6 @@ func resourceKeycloakOpenidClient() *schema.Resource { Optional: true, Default: false, }, - "service_accounts_enabled": { - Type: schema.TypeBool, - Optional: true, - Default: false, - }, "valid_redirect_uris": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, @@ -88,11 +85,48 @@ func resourceKeycloakOpenidClient() *schema.Resource { Set: schema.HashString, Optional: true, }, + "service_accounts_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "service_account_user_id": { + Type: schema.TypeString, + Computed: true, + }, + "resource_server_id": { + Type: schema.TypeString, + Computed: true, + }, + "authorization": { + Type: schema.TypeSet, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "policy_enforcement_mode": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(keycloakOpenidClientAuthorizationPolicyEnforcementMode, false), + }, + "allow_remote_resource_management": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "keep_defaults": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + }, + }, + }, }, } } -func getOpenidClientFromData(data *schema.ResourceData) *keycloak.OpenidClient { +func getOpenidClientFromData(data *schema.ResourceData) (*keycloak.OpenidClient, error) { validRedirectUris := make([]string, 0) webOrigins := make([]string, 0) @@ -124,6 +158,18 @@ func getOpenidClientFromData(data *schema.ResourceData) *keycloak.OpenidClient { WebOrigins: webOrigins, } + if !openidClient.ImplicitFlowEnabled && !openidClient.StandardFlowEnabled { + if _, ok := data.GetOk("valid_redirect_uris"); ok { + return nil, errors.New("valid_redirect_uris cannot be set when standard or implicit flow is not enabled") + } + } + + if !openidClient.ImplicitFlowEnabled && !openidClient.StandardFlowEnabled && !openidClient.DirectAccessGrantsEnabled { + if _, ok := data.GetOk("web_origins"); ok { + return nil, errors.New("web_origins cannot be set when standard or implicit flow is not enabled") + } + } + // access type if accessType := data.Get("access_type").(string); accessType == "PUBLIC" { openidClient.PublicClient = true @@ -131,12 +177,31 @@ func getOpenidClientFromData(data *schema.ResourceData) *keycloak.OpenidClient { openidClient.BearerOnly = true } - return openidClient + if v, ok := data.GetOk("authorization"); ok { + openidClient.AuthorizationServicesEnabled = true + authorizationSettingsData := v.(*schema.Set).List()[0] + authorizationSettings := authorizationSettingsData.(map[string]interface{}) + openidClient.AuthorizationSettings = &keycloak.OpenidClientAuthorizationSettings{ + PolicyEnforcementMode: authorizationSettings["policy_enforcement_mode"].(string), + AllowRemoteResourceManagement: authorizationSettings["allow_remote_resource_management"].(bool), + KeepDefaults: authorizationSettings["keep_defaults"].(bool), + } + } else { + openidClient.AuthorizationServicesEnabled = false + } + return openidClient, nil } -func setOpenidClientData(data *schema.ResourceData, client *keycloak.OpenidClient) { +func setOpenidClientData(keycloakClient *keycloak.KeycloakClient, data *schema.ResourceData, client *keycloak.OpenidClient) error { + var serviceAccountUserId string + if client.ServiceAccountsEnabled { + serviceAccountUser, err := keycloakClient.GetOpenidClientServiceAccountUserId(client.RealmId, client.Id) + if err != nil { + return err + } + serviceAccountUserId = serviceAccountUser.Id + } data.SetId(client.Id) - data.Set("client_id", client.ClientId) data.Set("realm_id", client.RealmId) data.Set("name", client.Name) @@ -149,6 +214,15 @@ func setOpenidClientData(data *schema.ResourceData, client *keycloak.OpenidClien data.Set("service_accounts_enabled", client.ServiceAccountsEnabled) data.Set("valid_redirect_uris", client.ValidRedirectUris) data.Set("web_origins", client.WebOrigins) + data.Set("authorization_services_enabled", client.AuthorizationServicesEnabled) + + if client.AuthorizationServicesEnabled { + data.Set("resource_server_id", client.Id) + } + + if client.ServiceAccountsEnabled { + data.Set("service_account_user_id", serviceAccountUserId) + } // access type if client.PublicClient { @@ -158,14 +232,18 @@ func setOpenidClientData(data *schema.ResourceData, client *keycloak.OpenidClien } else { data.Set("access_type", "CONFIDENTIAL") } + return nil } func resourceKeycloakOpenidClientCreate(data *schema.ResourceData, meta interface{}) error { keycloakClient := meta.(*keycloak.KeycloakClient) - client := getOpenidClientFromData(data) + client, err := getOpenidClientFromData(data) + if err != nil { + return err + } - err := keycloakClient.ValidateOpenidClient(client) + err = keycloakClient.ValidateOpenidClient(client) if err != nil { return err } @@ -175,7 +253,10 @@ func resourceKeycloakOpenidClientCreate(data *schema.ResourceData, meta interfac return err } - setOpenidClientData(data, client) + err = setOpenidClientData(keycloakClient, data, client) + if err != nil { + return err + } return resourceKeycloakOpenidClientRead(data, meta) } @@ -191,7 +272,10 @@ func resourceKeycloakOpenidClientRead(data *schema.ResourceData, meta interface{ return handleNotFoundError(err, data) } - setOpenidClientData(data, client) + err = setOpenidClientData(keycloakClient, data, client) + if err != nil { + return err + } return nil } @@ -199,9 +283,12 @@ func resourceKeycloakOpenidClientRead(data *schema.ResourceData, meta interface{ func resourceKeycloakOpenidClientUpdate(data *schema.ResourceData, meta interface{}) error { keycloakClient := meta.(*keycloak.KeycloakClient) - client := getOpenidClientFromData(data) + client, err := getOpenidClientFromData(data) + if err != nil { + return err + } - err := keycloakClient.ValidateOpenidClient(client) + err = keycloakClient.ValidateOpenidClient(client) if err != nil { return err } @@ -211,7 +298,10 @@ func resourceKeycloakOpenidClientUpdate(data *schema.ResourceData, meta interfac return err } - setOpenidClientData(data, client) + err = setOpenidClientData(keycloakClient, data, client) + if err != nil { + return err + } return nil } diff --git a/provider/resource_keycloak_openid_client_authorization_permission.go b/provider/resource_keycloak_openid_client_authorization_permission.go new file mode 100644 index 00000000..0ce9f43a --- /dev/null +++ b/provider/resource_keycloak_openid_client_authorization_permission.go @@ -0,0 +1,176 @@ +package provider + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "github.com/mrparkers/terraform-provider-keycloak/keycloak" + "strings" +) + +var ( + keycloakOpenidClientResourcePermissionDecisionStrategies = []string{"UNANIMOUS", "AFFIRMATIVE", "CONSENSUS"} + keycloakOpenidClientPermissionTypes = []string{"resource", "scope"} +) + +func resourceKeycloakOpenidClientAuthorizationPermission() *schema.Resource { + return &schema.Resource{ + Create: resourceKeycloakOpenidClientAuthorizationPermissionCreate, + Read: resourceKeycloakOpenidClientAuthorizationPermissionRead, + Delete: resourceKeycloakOpenidClientAuthorizationPermissionDelete, + Update: resourceKeycloakOpenidClientAuthorizationPermissionUpdate, + Importer: &schema.ResourceImporter{ + State: resourceKeycloakOpenidClientAuthorizationPermissionImport, + }, + Schema: map[string]*schema.Schema{ + "resource_server_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "realm_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "decision_strategy": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(keycloakOpenidClientResourcePermissionDecisionStrategies, false), + Default: "UNANIMOUS", + }, + "policies": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "resources": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "type": { + Type: schema.TypeString, + Optional: true, + Default: "resource", + ValidateFunc: validation.StringInSlice(keycloakOpenidClientPermissionTypes, false), + }, + }, + } +} + +func getOpenidClientAuthorizationPermissionFromData(data *schema.ResourceData) *keycloak.OpenidClientAuthorizationPermission { + var policies []string + var resources []string + if v, ok := data.GetOk("resources"); ok { + for _, resource := range v.(*schema.Set).List() { + resources = append(resources, resource.(string)) + } + } + if v, ok := data.GetOk("policies"); ok { + for _, policy := range v.(*schema.Set).List() { + policies = append(policies, policy.(string)) + } + } + permission := keycloak.OpenidClientAuthorizationPermission{ + Id: data.Id(), + ResourceServerId: data.Get("resource_server_id").(string), + RealmId: data.Get("realm_id").(string), + Description: data.Get("description").(string), + Name: data.Get("name").(string), + DecisionStrategy: data.Get("decision_strategy").(string), + Type: data.Get("type").(string), + Policies: policies, + Resources: resources, + } + return &permission +} + +func setOpenidClientAuthorizationPermissionData(data *schema.ResourceData, permission *keycloak.OpenidClientAuthorizationPermission) { + data.SetId(permission.Id) + data.Set("resource_server_id", permission.ResourceServerId) + data.Set("realm_id", permission.RealmId) + data.Set("description", permission.Description) + data.Set("name", permission.Name) + data.Set("decision_strategy", permission.DecisionStrategy) + data.Set("type", permission.Type) + data.Set("policies", permission.Policies) + data.Set("resources", permission.Resources) +} + +func resourceKeycloakOpenidClientAuthorizationPermissionCreate(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + permission := getOpenidClientAuthorizationPermissionFromData(data) + + err := keycloakClient.NewOpenidClientAuthorizationPermission(permission) + if err != nil { + return err + } + + setOpenidClientAuthorizationPermissionData(data, permission) + + return resourceKeycloakOpenidClientAuthorizationPermissionRead(data, meta) +} + +func resourceKeycloakOpenidClientAuthorizationPermissionRead(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + realmId := data.Get("realm_id").(string) + resourceServerId := data.Get("resource_server_id").(string) + id := data.Id() + + permission, err := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, resourceServerId, id) + if err != nil { + return handleNotFoundError(err, data) + } + + setOpenidClientAuthorizationPermissionData(data, permission) + + return nil +} + +func resourceKeycloakOpenidClientAuthorizationPermissionUpdate(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + permission := getOpenidClientAuthorizationPermissionFromData(data) + + err := keycloakClient.UpdateOpenidClientAuthorizationPermission(permission) + if err != nil { + return err + } + + setOpenidClientAuthorizationPermissionData(data, permission) + + return nil +} + +func resourceKeycloakOpenidClientAuthorizationPermissionDelete(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + realmId := data.Get("realm_id").(string) + resourceServerId := data.Get("resource_server_id").(string) + id := data.Id() + + return keycloakClient.DeleteOpenidClientAuthorizationPermission(realmId, resourceServerId, id) +} + +func resourceKeycloakOpenidClientAuthorizationPermissionImport(d *schema.ResourceData, _ interface{}) ([]*schema.ResourceData, error) { + parts := strings.Split(d.Id(), "/") + if len(parts) != 3 { + return nil, fmt.Errorf("Invalid import. Supported import formats: {{realmId}}/{{resourceServerId}}/{{permissionId}}") + } + d.Set("realm_id", parts[0]) + d.Set("resource_server_id", parts[1]) + d.SetId(parts[3]) + + return []*schema.ResourceData{d}, nil +} diff --git a/provider/resource_keycloak_openid_client_authorization_permission_test.go b/provider/resource_keycloak_openid_client_authorization_permission_test.go new file mode 100644 index 00000000..eaf373bd --- /dev/null +++ b/provider/resource_keycloak_openid_client_authorization_permission_test.go @@ -0,0 +1,279 @@ +package provider + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/mrparkers/terraform-provider-keycloak/keycloak" + "testing" +) + +func TestAccKeycloakOpenidClientAuthorizationPermission_basic(t *testing.T) { + realmName := "terraform-" + acctest.RandString(10) + clientId := "terraform-" + acctest.RandString(10) + resourceName := "terraform-" + acctest.RandString(10) + permissionName := "terraform-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakOpenidClientAuthorizationPermissionDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakOpenidClientAuthorizationPermission_basic(realmName, clientId, resourceName, permissionName), + Check: testAccCheckKeycloakOpenidClientAuthorizationPermissionExists("keycloak_openid_client_authorization_permission.test"), + }, + }, + }) +} + +func TestAccKeycloakOpenidClientAuthorizationPermission_createAfterManualDestroy(t *testing.T) { + var authorizationPermission = &keycloak.OpenidClientAuthorizationPermission{} + + realmName := "terraform-" + acctest.RandString(10) + clientId := "terraform-" + acctest.RandString(10) + resourceName := "terraform-" + acctest.RandString(10) + permissionName := "terraform-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakOpenidClientAuthorizationPermissionDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakOpenidClientAuthorizationPermission_basic(realmName, clientId, resourceName, permissionName), + Check: testAccCheckKeycloakOpenidClientAuthorizationPermissionFetch("keycloak_openid_client_authorization_permission.test", authorizationPermission), + }, + { + PreConfig: func() { + keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient) + + err := keycloakClient.DeleteOpenidClientAuthorizationPermission(authorizationPermission.RealmId, authorizationPermission.ResourceServerId, authorizationPermission.Id) + if err != nil { + t.Fatal(err) + } + }, + Config: testKeycloakOpenidClientAuthorizationPermission_basic(realmName, clientId, resourceName, permissionName), + Check: testAccCheckKeycloakOpenidClientAuthorizationPermissionExists("keycloak_openid_client_authorization_permission.test"), + }, + }, + }) +} + +func TestAccKeycloakOpenidClientAuthorizationPermission_basicUpdateRealm(t *testing.T) { + firstRealm := "terraform-" + acctest.RandString(10) + secondRealm := "terraform-" + acctest.RandString(10) + clientId := "terraform-" + acctest.RandString(10) + resourceName := "terraform-" + acctest.RandString(10) + permissionName := "terraform-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakOpenidClientAuthorizationPermissionDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakOpenidClientAuthorizationPermission_basic(firstRealm, clientId, resourceName, permissionName), + Check: resource.ComposeTestCheckFunc( + testAccCheckKeycloakOpenidClientAuthorizationPermissionExists("keycloak_openid_client_authorization_permission.test"), + resource.TestCheckResourceAttr("keycloak_openid_client_authorization_permission.test", "realm_id", firstRealm), + ), + }, + { + Config: testKeycloakOpenidClientAuthorizationPermission_basic(secondRealm, clientId, resourceName, permissionName), + Check: resource.ComposeTestCheckFunc( + testAccCheckKeycloakOpenidClientAuthorizationPermissionExists("keycloak_openid_client_authorization_permission.test"), + resource.TestCheckResourceAttr("keycloak_openid_client_authorization_permission.test", "realm_id", secondRealm), + ), + }, + }, + }) +} + +func TestAccKeycloakOpenidClientAuthorizationPermission_basicUpdateAll(t *testing.T) { + realmName := "terraform-" + acctest.RandString(10) + clientId := "terraform-" + acctest.RandString(10) + + firstAuthrorizationPermission := &keycloak.OpenidClientAuthorizationPermission{ + RealmId: realmName, + Name: acctest.RandString(10), + Description: acctest.RandString(10), + } + + secondAuthrorizationPermission := &keycloak.OpenidClientAuthorizationPermission{ + RealmId: realmName, + Name: acctest.RandString(10), + Description: acctest.RandString(10), + } + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakOpenidClientAuthorizationPermissionDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakOpenidClientAuthorizationPermission_basicFromInterface(clientId, firstAuthrorizationPermission, acctest.RandString(10)), + Check: testAccCheckKeycloakOpenidClientAuthorizationPermissionExists("keycloak_openid_client_authorization_permission.test"), + }, + { + Config: testKeycloakOpenidClientAuthorizationPermission_basicFromInterface(clientId, secondAuthrorizationPermission, acctest.RandString(10)), + Check: testAccCheckKeycloakOpenidClientAuthorizationPermissionExists("keycloak_openid_client_authorization_permission.test"), + }, + }, + }) +} + +func testAccCheckKeycloakOpenidClientAuthorizationPermissionExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, err := getKeycloakOpenidClientAuthorizationPermissionFromState(s, resourceName) + if err != nil { + return err + } + + return nil + } +} + +func testAccCheckKeycloakOpenidClientAuthorizationPermissionFetch(resourceName string, mapper *keycloak.OpenidClientAuthorizationPermission) resource.TestCheckFunc { + return func(s *terraform.State) error { + fetchedMapper, err := getKeycloakOpenidClientAuthorizationPermissionFromState(s, resourceName) + if err != nil { + return err + } + + mapper.ResourceServerId = fetchedMapper.ResourceServerId + mapper.RealmId = fetchedMapper.RealmId + mapper.Id = fetchedMapper.Id + + return nil + } +} + +func testAccCheckKeycloakOpenidClientAuthorizationPermissionDestroy() resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "keycloak_openid_client_authorization_permission" { + continue + } + + realmId := rs.Primary.Attributes["realm_id"] + resourceServerId := rs.Primary.Attributes["resource_server_id"] + id := rs.Primary.ID + + keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient) + + authorizationPermission, _ := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, resourceServerId, id) + if authorizationPermission != nil { + return fmt.Errorf("test config with id %s still exists", id) + } + } + + return nil + } +} + +func getKeycloakOpenidClientAuthorizationPermissionFromState(s *terraform.State, resourceName string) (*keycloak.OpenidClientAuthorizationPermission, error) { + keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient) + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return nil, fmt.Errorf("resource not found: %s", resourceName) + } + + realmId := rs.Primary.Attributes["realm_id"] + resourceServerId := rs.Primary.Attributes["resource_server_id"] + id := rs.Primary.ID + + authorizationPermission, err := keycloakClient.GetOpenidClientAuthorizationPermission(realmId, resourceServerId, id) + if err != nil { + return nil, fmt.Errorf("error getting authorization permission config with id %s: %s", id, err) + } + + return authorizationPermission, nil +} + +func testKeycloakOpenidClientAuthorizationPermission_basic(realm, clientId, resourceName, permissionName string) string { + return fmt.Sprintf(` +resource keycloak_realm test { + realm = "%s" +} + +resource keycloak_openid_client test { + client_id = "%s" + realm_id = "${keycloak_realm.test.id}" + access_type = "CONFIDENTIAL" + service_accounts_enabled = true + authorization { + policy_enforcement_mode = "ENFORCING" + } +} + +data keycloak_openid_client_authorization_policy default { + realm_id = "${keycloak_realm.test.id}" + resource_server_id = "${keycloak_openid_client.test.resource_server_id}" + name = "default" +} + +resource keycloak_openid_client_authorization_resource test { + resource_server_id = "${keycloak_openid_client.test.resource_server_id}" + name = "%s" + realm_id = "${keycloak_realm.test.id}" + + uris = [ + "/endpoint/*" + ] +} + +resource keycloak_openid_client_authorization_permission test { + resource_server_id = "${keycloak_openid_client.test.resource_server_id}" + realm_id = "${keycloak_realm.test.id}" + name = "%s" + policies = ["${data.keycloak_openid_client_authorization_policy.default.id}"] + resources = ["${keycloak_openid_client_authorization_resource.test.id}"] +} + `, realm, clientId, resourceName, permissionName) +} + +func testKeycloakOpenidClientAuthorizationPermission_basicFromInterface(clientId string, authorizationPermission *keycloak.OpenidClientAuthorizationPermission, resourceName string) string { + return fmt.Sprintf(` +resource keycloak_realm test { + realm = "%s" +} + +resource keycloak_openid_client test { + client_id = "%s" + realm_id = "${keycloak_realm.test.id}" + access_type = "CONFIDENTIAL" + service_accounts_enabled = true + authorization { + policy_enforcement_mode = "ENFORCING" + } +} + +data keycloak_openid_client_authorization_policy default { + realm_id = "${keycloak_realm.test.id}" + resource_server_id = "${keycloak_openid_client.test.resource_server_id}" + name = "default" +} + +resource keycloak_openid_client_authorization_resource resource { + resource_server_id = "${keycloak_openid_client.test.resource_server_id}" + name = "%s" + realm_id = "${keycloak_realm.test.id}" + + uris = [ + "/endpoint/*" + ] +} + +resource keycloak_openid_client_authorization_permission test { + resource_server_id = "${keycloak_openid_client.test.resource_server_id}" + realm_id = "${keycloak_realm.test.id}" + name = "%s" + policies = ["${data.keycloak_openid_client_authorization_policy.default.id}"] + resources = ["${keycloak_openid_client_authorization_resource.resource.id}"] + description = "%s" +} + `, authorizationPermission.RealmId, clientId, resourceName, authorizationPermission.Name, authorizationPermission.Description) +} diff --git a/provider/resource_keycloak_openid_client_authorization_resource.go b/provider/resource_keycloak_openid_client_authorization_resource.go new file mode 100644 index 00000000..e7aad5da --- /dev/null +++ b/provider/resource_keycloak_openid_client_authorization_resource.go @@ -0,0 +1,191 @@ +package provider + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/mrparkers/terraform-provider-keycloak/keycloak" + "strings" +) + +func resourceKeycloakOpenidClientAuthorizationResource() *schema.Resource { + return &schema.Resource{ + Create: resourceKeycloakOpenidClientAuthorizationResourceCreate, + Read: resourceKeycloakOpenidClientAuthorizationResourceRead, + Delete: resourceKeycloakOpenidClientAuthorizationResourceDelete, + Update: resourceKeycloakOpenidClientAuthorizationResourceUpdate, + Importer: &schema.ResourceImporter{ + State: resourceKeycloakOpenidClientAuthorizationResourceImport, + }, + Schema: map[string]*schema.Schema{ + "resource_server_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "realm_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "display_name": { + Type: schema.TypeString, + Optional: true, + }, + "uris": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "icon_uri": { + Type: schema.TypeString, + Optional: true, + }, + "owner_managed_access": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "scopes": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "type": { + Type: schema.TypeString, + Optional: true, + }, + "attributes": { + Type: schema.TypeMap, + Optional: true, + }, + }, + } +} + +func getOpenidClientAuthorizationResourceFromData(data *schema.ResourceData) *keycloak.OpenidClientAuthorizationResource { + var uris []string + var scopes []keycloak.OpenidClientAuthorizationScope + attributes := map[string][]string{} + if v, ok := data.GetOk("uris"); ok { + for _, uri := range v.(*schema.Set).List() { + uris = append(uris, uri.(string)) + } + } + if v, ok := data.GetOk("scopes"); ok { + for _, scope := range v.(*schema.Set).List() { + scopes = append(scopes, keycloak.OpenidClientAuthorizationScope{ + Name: scope.(string), + }) + } + } + if v, ok := data.GetOk("attributes"); ok { + for key, value := range v.(map[string]interface{}) { + attributes[key] = strings.Split(value.(string), ",") + } + } + resource := keycloak.OpenidClientAuthorizationResource{ + Id: data.Id(), + DisplayName: data.Get("display_name").(string), + Name: data.Get("name").(string), + IconUri: data.Get("icon_uri").(string), + OwnerManagedAccess: data.Get("owner_managed_access").(bool), + Type: data.Get("type").(string), + ResourceServerId: data.Get("resource_server_id").(string), + RealmId: data.Get("realm_id").(string), + Uris: uris, + Scopes: scopes, + Attributes: attributes, + } + return &resource +} + +func setOpenidClientAuthorizationResourceData(data *schema.ResourceData, resource *keycloak.OpenidClientAuthorizationResource) { + scopes := []string{} + for _, scope := range resource.Scopes { + scopes = append(scopes, scope.Name) + } + data.SetId(resource.Id) + data.Set("resource_server_id", resource.ResourceServerId) + data.Set("realm_id", resource.RealmId) + data.Set("display_name", resource.DisplayName) + data.Set("name", resource.Name) + data.Set("icon_uri", resource.IconUri) + data.Set("owner_managed_access", resource.OwnerManagedAccess) + data.Set("type", resource.Type) + data.Set("uris", resource.Uris) + data.Set("attributes", resource.Attributes) + data.Set("scopes", scopes) +} + +func resourceKeycloakOpenidClientAuthorizationResourceCreate(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + resource := getOpenidClientAuthorizationResourceFromData(data) + + err := keycloakClient.NewOpenidClientAuthorizationResource(resource) + if err != nil { + return err + } + + setOpenidClientAuthorizationResourceData(data, resource) + + return resourceKeycloakOpenidClientAuthorizationResourceRead(data, meta) +} + +func resourceKeycloakOpenidClientAuthorizationResourceRead(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + realmId := data.Get("realm_id").(string) + resourceServerId := data.Get("resource_server_id").(string) + id := data.Id() + + resource, err := keycloakClient.GetOpenidClientAuthorizationResource(realmId, resourceServerId, id) + if err != nil { + return handleNotFoundError(err, data) + } + + setOpenidClientAuthorizationResourceData(data, resource) + + return nil +} + +func resourceKeycloakOpenidClientAuthorizationResourceUpdate(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + resource := getOpenidClientAuthorizationResourceFromData(data) + + err := keycloakClient.UpdateOpenidClientAuthorizationResource(resource) + if err != nil { + return err + } + + setOpenidClientAuthorizationResourceData(data, resource) + + return nil +} + +func resourceKeycloakOpenidClientAuthorizationResourceDelete(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + realmId := data.Get("realm_id").(string) + resourceServerId := data.Get("resource_server_id").(string) + id := data.Id() + + return keycloakClient.DeleteOpenidClientAuthorizationResource(realmId, resourceServerId, id) +} + +func resourceKeycloakOpenidClientAuthorizationResourceImport(d *schema.ResourceData, _ interface{}) ([]*schema.ResourceData, error) { + parts := strings.Split(d.Id(), "/") + if len(parts) != 3 { + return nil, fmt.Errorf("Invalid import. Supported import formats: {{realmId}}/{{resourceServerId}}/{{authorizationResourceId}}") + } + d.Set("realm_id", parts[0]) + d.Set("resource_server_id", parts[1]) + d.SetId(parts[3]) + + return []*schema.ResourceData{d}, nil +} diff --git a/provider/resource_keycloak_openid_client_authorization_resource_test.go b/provider/resource_keycloak_openid_client_authorization_resource_test.go new file mode 100644 index 00000000..7757cb23 --- /dev/null +++ b/provider/resource_keycloak_openid_client_authorization_resource_test.go @@ -0,0 +1,257 @@ +package provider + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/mrparkers/terraform-provider-keycloak/keycloak" + "testing" +) + +func TestAccKeycloakOpenidClientAuthorizationResource_basic(t *testing.T) { + realmName := "terraform-" + acctest.RandString(10) + clientId := "terraform-" + acctest.RandString(10) + resourceName := "terraform-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakOpenidClientAuthorizationResourceDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakOpenidClientAuthorizationResource_basic(realmName, clientId, resourceName), + Check: testAccCheckKeycloakOpenidClientAuthorizationResourceExists("keycloak_openid_client_authorization_resource.test"), + }, + }, + }) +} + +func TestAccKeycloakOpenidClientAuthorizationResource_createAfterManualDestroy(t *testing.T) { + var authorizationResource = &keycloak.OpenidClientAuthorizationResource{} + + realmName := "terraform-" + acctest.RandString(10) + clientId := "terraform-" + acctest.RandString(10) + resourceName := "terraform-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakOpenidClientAuthorizationResourceDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakOpenidClientAuthorizationResource_basic(realmName, clientId, resourceName), + Check: testAccCheckKeycloakOpenidClientAuthorizationResourceFetch("keycloak_openid_client_authorization_resource.test", authorizationResource), + }, + { + PreConfig: func() { + keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient) + + err := keycloakClient.DeleteOpenidClientAuthorizationResource(authorizationResource.RealmId, authorizationResource.ResourceServerId, authorizationResource.Id) + if err != nil { + t.Fatal(err) + } + }, + Config: testKeycloakOpenidClientAuthorizationResource_basic(realmName, clientId, resourceName), + Check: testAccCheckKeycloakOpenidClientAuthorizationResourceExists("keycloak_openid_client_authorization_resource.test"), + }, + }, + }) +} + +func TestAccKeycloakOpenidClientAuthorizationResource_basicUpdateRealm(t *testing.T) { + firstRealm := "terraform-" + acctest.RandString(10) + secondRealm := "terraform-" + acctest.RandString(10) + clientId := "terraform-" + acctest.RandString(10) + resourceName := "terraform-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakOpenidClientAuthorizationResourceDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakOpenidClientAuthorizationResource_basic(firstRealm, clientId, resourceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckKeycloakOpenidClientAuthorizationResourceExists("keycloak_openid_client_authorization_resource.test"), + resource.TestCheckResourceAttr("keycloak_openid_client_authorization_resource.test", "realm_id", firstRealm), + ), + }, + { + Config: testKeycloakOpenidClientAuthorizationResource_basic(secondRealm, clientId, resourceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckKeycloakOpenidClientAuthorizationResourceExists("keycloak_openid_client_authorization_resource.test"), + resource.TestCheckResourceAttr("keycloak_openid_client_authorization_resource.test", "realm_id", secondRealm), + ), + }, + }, + }) +} + +func TestAccKeycloakOpenidClientAuthorizationResource_basicUpdateAll(t *testing.T) { + realmName := "terraform-" + acctest.RandString(10) + clientId := "terraform-" + acctest.RandString(10) + ownerManagedAccess := randomBool() + + firstAuthrorizationResource := &keycloak.OpenidClientAuthorizationResource{ + RealmId: realmName, + Name: acctest.RandString(10), + DisplayName: acctest.RandString(10), + IconUri: acctest.RandString(10), + Type: acctest.RandString(10), + OwnerManagedAccess: ownerManagedAccess, + } + + secondAuthrorizationResource := &keycloak.OpenidClientAuthorizationResource{ + RealmId: realmName, + Name: acctest.RandString(10), + DisplayName: acctest.RandString(10), + IconUri: acctest.RandString(10), + Type: acctest.RandString(10), + OwnerManagedAccess: !ownerManagedAccess, + } + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakOpenidClientAuthorizationResourceDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakOpenidClientAuthorizationResource_basicFromInterface(clientId, firstAuthrorizationResource), + Check: testAccCheckKeycloakOpenidClientAuthorizationResourceExists("keycloak_openid_client_authorization_resource.test"), + }, + { + Config: testKeycloakOpenidClientAuthorizationResource_basicFromInterface(clientId, secondAuthrorizationResource), + Check: testAccCheckKeycloakOpenidClientAuthorizationResourceExists("keycloak_openid_client_authorization_resource.test"), + }, + }, + }) +} + +func testAccCheckKeycloakOpenidClientAuthorizationResourceExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, err := getKeycloakOpenidClientAuthorizationResourceFromState(s, resourceName) + if err != nil { + return err + } + + return nil + } +} + +func testAccCheckKeycloakOpenidClientAuthorizationResourceFetch(resourceName string, mapper *keycloak.OpenidClientAuthorizationResource) resource.TestCheckFunc { + return func(s *terraform.State) error { + fetchedMapper, err := getKeycloakOpenidClientAuthorizationResourceFromState(s, resourceName) + if err != nil { + return err + } + + mapper.ResourceServerId = fetchedMapper.ResourceServerId + mapper.RealmId = fetchedMapper.RealmId + mapper.Id = fetchedMapper.Id + + return nil + } +} + +func testAccCheckKeycloakOpenidClientAuthorizationResourceDestroy() resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "keycloak_openid_client_authorization_resource" { + continue + } + + realmId := rs.Primary.Attributes["realm_id"] + resourceServerId := rs.Primary.Attributes["resource_server_id"] + id := rs.Primary.ID + + keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient) + + authorizationResource, _ := keycloakClient.GetOpenidClientAuthorizationResource(realmId, resourceServerId, id) + if authorizationResource != nil { + return fmt.Errorf("test config with id %s still exists", id) + } + } + + return nil + } +} + +func getKeycloakOpenidClientAuthorizationResourceFromState(s *terraform.State, resourceName string) (*keycloak.OpenidClientAuthorizationResource, error) { + keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient) + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return nil, fmt.Errorf("resource not found: %s", resourceName) + } + + realmId := rs.Primary.Attributes["realm_id"] + resourceServerId := rs.Primary.Attributes["resource_server_id"] + id := rs.Primary.ID + + authorizationResource, err := keycloakClient.GetOpenidClientAuthorizationResource(realmId, resourceServerId, id) + if err != nil { + return nil, fmt.Errorf("error getting authorization resource config with id %s: %s", id, err) + } + + return authorizationResource, nil +} + +func testKeycloakOpenidClientAuthorizationResource_basic(realm, clientId, resourceName string) string { + return fmt.Sprintf(` +resource keycloak_realm test { + realm = "%s" +} + +resource keycloak_openid_client test { + client_id = "%s" + realm_id = "${keycloak_realm.test.id}" + access_type = "CONFIDENTIAL" + service_accounts_enabled = true + authorization { + policy_enforcement_mode = "ENFORCING" + } +} + +resource keycloak_openid_client_authorization_resource test { + resource_server_id = "${keycloak_openid_client.test.resource_server_id}" + name = "%s" + realm_id = "${keycloak_realm.test.id}" + + uris = [ + "/endpoint/*" + ] +} + `, realm, clientId, resourceName) +} + +func testKeycloakOpenidClientAuthorizationResource_basicFromInterface(clientId string, authorizationResource *keycloak.OpenidClientAuthorizationResource) string { + return fmt.Sprintf(` +resource keycloak_realm test { + realm = "%s" +} + +resource keycloak_openid_client test { + client_id = "%s" + realm_id = "${keycloak_realm.test.id}" + access_type = "CONFIDENTIAL" + service_accounts_enabled = true + authorization { + policy_enforcement_mode = "ENFORCING" + } +} + +resource keycloak_openid_client_authorization_resource test { + resource_server_id = "${keycloak_openid_client.test.resource_server_id}" + name = "%s" + realm_id = "${keycloak_realm.test.id}" + display_name = "%s" + icon_uri = "%s" + owner_managed_access = %t + type = "%s" + uris = [ + "/test/" + ] +} + `, authorizationResource.RealmId, clientId, authorizationResource.Name, authorizationResource.DisplayName, authorizationResource.IconUri, authorizationResource.OwnerManagedAccess, authorizationResource.Type) +} diff --git a/provider/resource_keycloak_openid_client_authorization_scope.go b/provider/resource_keycloak_openid_client_authorization_scope.go new file mode 100644 index 00000000..723e29cf --- /dev/null +++ b/provider/resource_keycloak_openid_client_authorization_scope.go @@ -0,0 +1,134 @@ +package provider + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/mrparkers/terraform-provider-keycloak/keycloak" + "strings" +) + +func resourceKeycloakOpenidClientAuthorizationScope() *schema.Resource { + return &schema.Resource{ + Create: resourceKeycloakOpenidClientAuthorizationScopeCreate, + Read: resourceKeycloakOpenidClientAuthorizationScopeRead, + Delete: resourceKeycloakOpenidClientAuthorizationScopeDelete, + Update: resourceKeycloakOpenidClientAuthorizationScopeUpdate, + Importer: &schema.ResourceImporter{ + State: resourceKeycloakOpenidClientAuthorizationScopeImport, + }, + Schema: map[string]*schema.Schema{ + "resource_server_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "realm_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "display_name": { + Type: schema.TypeString, + Optional: true, + }, + "icon_uri": { + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func getOpenidClientAuthorizationScopeFromData(data *schema.ResourceData) *keycloak.OpenidClientAuthorizationScope { + scope := keycloak.OpenidClientAuthorizationScope{ + DisplayName: data.Get("display_name").(string), + Name: data.Get("name").(string), + IconUri: data.Get("icon_uri").(string), + Id: data.Id(), + ResourceServerId: data.Get("resource_server_id").(string), + RealmId: data.Get("realm_id").(string), + } + return &scope +} + +func setOpenidClientAuthorizationScopeData(data *schema.ResourceData, scope *keycloak.OpenidClientAuthorizationScope) { + data.SetId(scope.Id) + data.Set("resource_server_id", scope.ResourceServerId) + data.Set("realm_id", scope.RealmId) + data.Set("display_name", scope.DisplayName) + data.Set("name", scope.Name) + data.Set("icon_uri", scope.IconUri) +} + +func resourceKeycloakOpenidClientAuthorizationScopeCreate(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + scope := getOpenidClientAuthorizationScopeFromData(data) + + err := keycloakClient.NewOpenidClientAuthorizationScope(scope) + if err != nil { + return err + } + + setOpenidClientAuthorizationScopeData(data, scope) + + return resourceKeycloakOpenidClientAuthorizationScopeRead(data, meta) +} + +func resourceKeycloakOpenidClientAuthorizationScopeRead(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + realmId := data.Get("realm_id").(string) + resourceServerId := data.Get("resource_server_id").(string) + id := data.Id() + + scope, err := keycloakClient.GetOpenidClientAuthorizationScope(realmId, resourceServerId, id) + if err != nil { + return handleNotFoundError(err, data) + } + + setOpenidClientAuthorizationScopeData(data, scope) + + return nil +} + +func resourceKeycloakOpenidClientAuthorizationScopeUpdate(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + scope := getOpenidClientAuthorizationScopeFromData(data) + + err := keycloakClient.UpdateOpenidClientAuthorizationScope(scope) + if err != nil { + return err + } + + setOpenidClientAuthorizationScopeData(data, scope) + + return nil +} + +func resourceKeycloakOpenidClientAuthorizationScopeDelete(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + realmId := data.Get("realm_id").(string) + resourceServerId := data.Get("resource_server_id").(string) + id := data.Id() + + return keycloakClient.DeleteOpenidClientAuthorizationScope(realmId, resourceServerId, id) +} + +func resourceKeycloakOpenidClientAuthorizationScopeImport(d *schema.ResourceData, _ interface{}) ([]*schema.ResourceData, error) { + parts := strings.Split(d.Id(), "/") + if len(parts) != 3 { + return nil, fmt.Errorf("Invalid import. Supported import formats: {{realmId}}/{{resourceServerId}}/{{authorizationScopeId}}") + } + d.Set("realm_id", parts[0]) + d.Set("resource_server_id", parts[1]) + d.SetId(parts[3]) + + return []*schema.ResourceData{d}, nil +} diff --git a/provider/resource_keycloak_openid_client_authorization_scope_test.go b/provider/resource_keycloak_openid_client_authorization_scope_test.go new file mode 100644 index 00000000..854322f7 --- /dev/null +++ b/provider/resource_keycloak_openid_client_authorization_scope_test.go @@ -0,0 +1,243 @@ +package provider + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/mrparkers/terraform-provider-keycloak/keycloak" + "testing" +) + +func TestAccKeycloakOpenidClientAuthorizationScope_basic(t *testing.T) { + realmName := "terraform-" + acctest.RandString(10) + clientId := "terraform-" + acctest.RandString(10) + scopeName := "terraform-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakOpenidClientAuthorizationScopeDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakOpenidClientAuthorizationScope_basic(realmName, clientId, scopeName), + Check: testAccCheckKeycloakOpenidClientAuthorizationScopeExists("keycloak_openid_client_authorization_scope.test"), + }, + }, + }) +} + +func TestAccKeycloakOpenidClientAuthorizationScope_createAfterManualDestroy(t *testing.T) { + var authorizationScope = &keycloak.OpenidClientAuthorizationScope{} + + realmName := "terraform-" + acctest.RandString(10) + clientId := "terraform-" + acctest.RandString(10) + scopeName := "terraform-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakOpenidClientAuthorizationScopeDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakOpenidClientAuthorizationScope_basic(realmName, clientId, scopeName), + Check: testAccCheckKeycloakOpenidClientAuthorizationScopeFetch("keycloak_openid_client_authorization_scope.test", authorizationScope), + }, + { + PreConfig: func() { + keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient) + + err := keycloakClient.DeleteOpenidClientAuthorizationScope(authorizationScope.RealmId, authorizationScope.ResourceServerId, authorizationScope.Id) + if err != nil { + t.Fatal(err) + } + }, + Config: testKeycloakOpenidClientAuthorizationScope_basic(realmName, clientId, scopeName), + Check: testAccCheckKeycloakOpenidClientAuthorizationScopeExists("keycloak_openid_client_authorization_scope.test"), + }, + }, + }) +} + +func TestAccKeycloakOpenidClientAuthorizationScope_basicUpdateRealm(t *testing.T) { + firstRealm := "terraform-" + acctest.RandString(10) + secondRealm := "terraform-" + acctest.RandString(10) + clientId := "terraform-" + acctest.RandString(10) + scopeName := "terraform-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakOpenidClientAuthorizationScopeDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakOpenidClientAuthorizationScope_basic(firstRealm, clientId, scopeName), + Check: resource.ComposeTestCheckFunc( + testAccCheckKeycloakOpenidClientAuthorizationScopeExists("keycloak_openid_client_authorization_scope.test"), + resource.TestCheckResourceAttr("keycloak_openid_client_authorization_scope.test", "realm_id", firstRealm), + ), + }, + { + Config: testKeycloakOpenidClientAuthorizationScope_basic(secondRealm, clientId, scopeName), + Check: resource.ComposeTestCheckFunc( + testAccCheckKeycloakOpenidClientAuthorizationScopeExists("keycloak_openid_client_authorization_scope.test"), + resource.TestCheckResourceAttr("keycloak_openid_client_authorization_scope.test", "realm_id", secondRealm), + ), + }, + }, + }) +} + +func TestAccKeycloakOpenidClientAuthorizationScope_basicUpdateAll(t *testing.T) { + realmName := "terraform-" + acctest.RandString(10) + clientId := "terraform-" + acctest.RandString(10) + + firstAuthrorizationScope := &keycloak.OpenidClientAuthorizationScope{ + RealmId: realmName, + Name: acctest.RandString(10), + DisplayName: acctest.RandString(10), + IconUri: acctest.RandString(10), + } + + secondAuthrorizationScope := &keycloak.OpenidClientAuthorizationScope{ + RealmId: realmName, + Name: acctest.RandString(10), + DisplayName: acctest.RandString(10), + IconUri: acctest.RandString(10), + } + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakOpenidClientAuthorizationScopeDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakOpenidClientAuthorizationScope_basicFromInterface(clientId, firstAuthrorizationScope), + Check: testAccCheckKeycloakOpenidClientAuthorizationScopeExists("keycloak_openid_client_authorization_scope.test"), + }, + { + Config: testKeycloakOpenidClientAuthorizationScope_basicFromInterface(clientId, secondAuthrorizationScope), + Check: testAccCheckKeycloakOpenidClientAuthorizationScopeExists("keycloak_openid_client_authorization_scope.test"), + }, + }, + }) +} + +func testAccCheckKeycloakOpenidClientAuthorizationScopeExists(scopeName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, err := getKeycloakOpenidClientAuthorizationScopeFromState(s, scopeName) + if err != nil { + return err + } + + return nil + } +} + +func testAccCheckKeycloakOpenidClientAuthorizationScopeFetch(scopeName string, authorizationScope *keycloak.OpenidClientAuthorizationScope) resource.TestCheckFunc { + return func(s *terraform.State) error { + fetchedAuthorizationScope, err := getKeycloakOpenidClientAuthorizationScopeFromState(s, scopeName) + if err != nil { + return err + } + + authorizationScope.ResourceServerId = fetchedAuthorizationScope.ResourceServerId + authorizationScope.RealmId = fetchedAuthorizationScope.RealmId + authorizationScope.Id = fetchedAuthorizationScope.Id + + return nil + } +} + +func testAccCheckKeycloakOpenidClientAuthorizationScopeDestroy() resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "keycloak_openid_client_authorization_scope" { + continue + } + + realmId := rs.Primary.Attributes["realm_id"] + resourceServerId := rs.Primary.Attributes["resource_server_id"] + id := rs.Primary.ID + + keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient) + + authorizationScope, _ := keycloakClient.GetOpenidClientAuthorizationScope(realmId, resourceServerId, id) + if authorizationScope != nil { + return fmt.Errorf("test config with id %s still exists", id) + } + } + + return nil + } +} + +func getKeycloakOpenidClientAuthorizationScopeFromState(s *terraform.State, scopeName string) (*keycloak.OpenidClientAuthorizationScope, error) { + keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient) + + rs, ok := s.RootModule().Resources[scopeName] + if !ok { + return nil, fmt.Errorf("resource not found: %s", scopeName) + } + + realmId := rs.Primary.Attributes["realm_id"] + resourceServerId := rs.Primary.Attributes["resource_server_id"] + id := rs.Primary.ID + + authorizationScope, err := keycloakClient.GetOpenidClientAuthorizationScope(realmId, resourceServerId, id) + if err != nil { + return nil, fmt.Errorf("error getting authorization scope config with id %s: %s", id, err) + } + + return authorizationScope, nil +} + +func testKeycloakOpenidClientAuthorizationScope_basic(realm, clientId, scopeName string) string { + return fmt.Sprintf(` +resource keycloak_realm test { + realm = "%s" +} + +resource keycloak_openid_client test { + client_id = "%s" + realm_id = "${keycloak_realm.test.id}" + access_type = "CONFIDENTIAL" + service_accounts_enabled = true + authorization { + policy_enforcement_mode = "ENFORCING" + } +} + +resource keycloak_openid_client_authorization_scope test { + resource_server_id = "${keycloak_openid_client.test.resource_server_id}" + name = "%s" + realm_id = "${keycloak_realm.test.id}" +} + `, realm, clientId, scopeName) +} + +func testKeycloakOpenidClientAuthorizationScope_basicFromInterface(clientId string, authorizationScope *keycloak.OpenidClientAuthorizationScope) string { + return fmt.Sprintf(` +resource keycloak_realm test { + realm = "%s" +} + +resource keycloak_openid_client test { + client_id = "%s" + realm_id = "${keycloak_realm.test.id}" + access_type = "CONFIDENTIAL" + service_accounts_enabled = true + authorization { + policy_enforcement_mode = "ENFORCING" + } +} + +resource keycloak_openid_client_authorization_scope test { + resource_server_id = "${keycloak_openid_client.test.resource_server_id}" + name = "%s" + realm_id = "${keycloak_realm.test.id}" + display_name = "%s" + icon_uri = "%s" +} + `, authorizationScope.RealmId, clientId, authorizationScope.Name, authorizationScope.DisplayName, authorizationScope.IconUri) +} diff --git a/provider/resource_keycloak_openid_client_default_scopes_test.go b/provider/resource_keycloak_openid_client_default_scopes_test.go index 33587437..9b1d63b6 100644 --- a/provider/resource_keycloak_openid_client_default_scopes_test.go +++ b/provider/resource_keycloak_openid_client_default_scopes_test.go @@ -407,8 +407,6 @@ resource "keycloak_openid_client" "client" { client_id = "%s" realm_id = "${keycloak_realm.realm.id}" access_type = "PUBLIC" - - valid_redirect_uris = ["foo"] } resource "keycloak_openid_client_scope" "client_scope" { @@ -442,8 +440,6 @@ resource "keycloak_openid_client" "client" { client_id = "%s" realm_id = "${keycloak_realm.realm.id}" access_type = "PUBLIC" - - valid_redirect_uris = ["foo"] } resource "keycloak_openid_client_scope" "client_scope" { @@ -465,8 +461,6 @@ resource "keycloak_openid_client" "client" { client_id = "%s" realm_id = "${keycloak_realm.realm.id}" access_type = "PUBLIC" - - valid_redirect_uris = ["foo"] } resource "keycloak_openid_client_scope" "client_scope" { @@ -523,8 +517,6 @@ resource "keycloak_openid_client" "client" { client_id = "%s" realm_id = "${keycloak_realm.realm.id}" access_type = "BEARER-ONLY" - - valid_redirect_uris = ["foo"] } resource "keycloak_openid_client_scope" "client_scope" { @@ -579,8 +571,6 @@ resource "keycloak_openid_client" "client" { client_id = "%s" realm_id = "${keycloak_realm.realm.id}" access_type = "PUBLIC" - - valid_redirect_uris = ["foo"] } %s diff --git a/provider/resource_keycloak_openid_client_optional_scopes_test.go b/provider/resource_keycloak_openid_client_optional_scopes_test.go index 994f7811..910eca59 100644 --- a/provider/resource_keycloak_openid_client_optional_scopes_test.go +++ b/provider/resource_keycloak_openid_client_optional_scopes_test.go @@ -408,8 +408,6 @@ resource "keycloak_openid_client" "client" { client_id = "%s" realm_id = "${keycloak_realm.realm.id}" access_type = "PUBLIC" - - valid_redirect_uris = ["foo"] } resource "keycloak_openid_client_scope" "client_scope" { @@ -443,8 +441,6 @@ resource "keycloak_openid_client" "client" { client_id = "%s" realm_id = "${keycloak_realm.realm.id}" access_type = "PUBLIC" - - valid_redirect_uris = ["foo"] } resource "keycloak_openid_client_scope" "client_scope" { @@ -466,8 +462,6 @@ resource "keycloak_openid_client" "client" { client_id = "%s" realm_id = "${keycloak_realm.realm.id}" access_type = "PUBLIC" - - valid_redirect_uris = ["foo"] } resource "keycloak_openid_client_scope" "client_scope" { @@ -524,8 +518,6 @@ resource "keycloak_openid_client" "client" { client_id = "%s" realm_id = "${keycloak_realm.realm.id}" access_type = "BEARER-ONLY" - - valid_redirect_uris = ["foo"] } resource "keycloak_openid_client_scope" "client_scope" { @@ -580,8 +572,6 @@ resource "keycloak_openid_client" "client" { client_id = "%s" realm_id = "${keycloak_realm.realm.id}" access_type = "PUBLIC" - - valid_redirect_uris = ["foo"] } %s diff --git a/provider/resource_keycloak_openid_client_service_account_role.go b/provider/resource_keycloak_openid_client_service_account_role.go new file mode 100644 index 00000000..75be439f --- /dev/null +++ b/provider/resource_keycloak_openid_client_service_account_role.go @@ -0,0 +1,112 @@ +package provider + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "github.com/mrparkers/terraform-provider-keycloak/keycloak" + "strings" +) + +func resourceKeycloakOpenidClientServiceAccountRole() *schema.Resource { + return &schema.Resource{ + Create: resourceKeycloakOpenidClientServiceAccountRoleCreate, + Read: resourceKeycloakOpenidClientServiceAccountRoleRead, + Delete: resourceKeycloakOpenidClientServiceAccountRoleDelete, + Importer: &schema.ResourceImporter{ + State: resourceKeycloakOpenidClientServiceAccountRoleImport, + }, + Schema: map[string]*schema.Schema{ + "service_account_user_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "realm_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "client_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "role": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func getOpenidClientServiceAccountRoleFromData(data *schema.ResourceData) *keycloak.OpenidClientServiceAccountRole { + return &keycloak.OpenidClientServiceAccountRole{ + Id: data.Id(), + ContainerId: data.Get("client_id").(string), + Name: data.Get("role").(string), + RealmId: data.Get("realm_id").(string), + ServiceAccountUserId: data.Get("service_account_user_id").(string), + } +} + +func setOpenidClientServiceAccountRoleData(data *schema.ResourceData, serviceAccountRole *keycloak.OpenidClientServiceAccountRole) { + data.SetId(serviceAccountRole.Id) + data.Set("realm_id", serviceAccountRole.RealmId) + data.Set("client_id", serviceAccountRole.ContainerId) + data.Set("service_account_user_id", serviceAccountRole.ServiceAccountUserId) + data.Set("role", serviceAccountRole.Name) +} + +func resourceKeycloakOpenidClientServiceAccountRoleCreate(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + serviceAccountRole := getOpenidClientServiceAccountRoleFromData(data) + err := keycloakClient.NewOpenidClientServiceAccountRole(serviceAccountRole) + if err != nil { + return err + } + setOpenidClientServiceAccountRoleData(data, serviceAccountRole) + return resourceKeycloakOpenidClientServiceAccountRoleRead(data, meta) +} + +func resourceKeycloakOpenidClientServiceAccountRoleRead(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + realmId := data.Get("realm_id").(string) + serviceAccountUserId := data.Get("service_account_user_id").(string) + clientId := data.Get("client_id").(string) + id := data.Id() + + serviceAccountRole, err := keycloakClient.GetOpenidClientServiceAccountRole(realmId, serviceAccountUserId, clientId, id) + if err != nil { + return handleNotFoundError(err, data) + } + + setOpenidClientServiceAccountRoleData(data, serviceAccountRole) + + return nil +} + +func resourceKeycloakOpenidClientServiceAccountRoleDelete(data *schema.ResourceData, meta interface{}) error { + keycloakClient := meta.(*keycloak.KeycloakClient) + + realmId := data.Get("realm_id").(string) + serviceAccountUserId := data.Get("service_account_user_id").(string) + clientId := data.Get("client_id").(string) + id := data.Id() + + return keycloakClient.DeleteOpenidClientServiceAccountRole(realmId, serviceAccountUserId, clientId, id) +} + +func resourceKeycloakOpenidClientServiceAccountRoleImport(d *schema.ResourceData, _ interface{}) ([]*schema.ResourceData, error) { + parts := strings.Split(d.Id(), "/") + if len(parts) != 3 { + return nil, fmt.Errorf("Invalid import. Supported import formats: {{realmId}}/{{serviceAccountUserId}}/{{clientId}}/{{role}}") + } + d.Set("realm_id", parts[0]) + d.Set("service_account_user_id", parts[1]) + d.Set("client_id", parts[2]) + d.SetId(parts[3]) + + return []*schema.ResourceData{d}, nil +} diff --git a/provider/resource_keycloak_openid_client_service_account_role_test.go b/provider/resource_keycloak_openid_client_service_account_role_test.go new file mode 100644 index 00000000..092e9166 --- /dev/null +++ b/provider/resource_keycloak_openid_client_service_account_role_test.go @@ -0,0 +1,186 @@ +package provider + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/mrparkers/terraform-provider-keycloak/keycloak" + "testing" +) + +func TestAccKeycloakOpenidClientServiceAccountRole_basic(t *testing.T) { + realmName := "terraform-" + acctest.RandString(10) + clientId := "terraform-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakOpenidClientServiceAccountRoleDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakOpenidClientServiceAccountRole_basic(realmName, clientId), + Check: testAccCheckKeycloakOpenidClientServiceAccountRoleExists("keycloak_openid_client_service_account_role.test"), + }, + }, + }) +} + +func TestAccKeycloakOpenidClientServiceAccountRole_createAfterManualDestroy(t *testing.T) { + var serviceAccountRole = &keycloak.OpenidClientServiceAccountRole{} + + realmName := "terraform-" + acctest.RandString(10) + clientId := "terraform-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakOpenidClientServiceAccountRoleDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakOpenidClientServiceAccountRole_basic(realmName, clientId), + Check: testAccCheckKeycloakOpenidClientServiceAccountRoleFetch("keycloak_openid_client_service_account_role.test", serviceAccountRole), + }, + { + PreConfig: func() { + keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient) + + err := keycloakClient.DeleteOpenidClientServiceAccountRole(serviceAccountRole.RealmId, serviceAccountRole.ServiceAccountUserId, serviceAccountRole.ContainerId, serviceAccountRole.Id) + if err != nil { + t.Fatal(err) + } + }, + Config: testKeycloakOpenidClientServiceAccountRole_basic(realmName, clientId), + Check: testAccCheckKeycloakOpenidClientServiceAccountRoleExists("keycloak_openid_client_service_account_role.test"), + }, + }, + }) +} + +func TestAccKeycloakOpenidClientServiceAccountRole_basicUpdateRealm(t *testing.T) { + firstRealm := "terraform-" + acctest.RandString(10) + secondRealm := "terraform-" + acctest.RandString(10) + clientId := "terraform-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckKeycloakOpenidClientServiceAccountRoleDestroy(), + Steps: []resource.TestStep{ + { + Config: testKeycloakOpenidClientServiceAccountRole_basic(firstRealm, clientId), + Check: resource.ComposeTestCheckFunc( + testAccCheckKeycloakOpenidClientServiceAccountRoleExists("keycloak_openid_client_service_account_role.test"), + resource.TestCheckResourceAttr("keycloak_openid_client_service_account_role.test", "realm_id", firstRealm), + ), + }, + { + Config: testKeycloakOpenidClientServiceAccountRole_basic(secondRealm, clientId), + Check: resource.ComposeTestCheckFunc( + testAccCheckKeycloakOpenidClientServiceAccountRoleExists("keycloak_openid_client_service_account_role.test"), + resource.TestCheckResourceAttr("keycloak_openid_client_service_account_role.test", "realm_id", secondRealm), + ), + }, + }, + }) +} + +func testAccCheckKeycloakOpenidClientServiceAccountRoleExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, err := getKeycloakOpenidClientServiceAccountRoleFromState(s, resourceName) + if err != nil { + return err + } + + return nil + } +} + +func testAccCheckKeycloakOpenidClientServiceAccountRoleFetch(resourceName string, serviceAccountRole *keycloak.OpenidClientServiceAccountRole) resource.TestCheckFunc { + return func(s *terraform.State) error { + fetchedServiceAccountRole, err := getKeycloakOpenidClientServiceAccountRoleFromState(s, resourceName) + if err != nil { + return err + } + + serviceAccountRole.ServiceAccountUserId = fetchedServiceAccountRole.ServiceAccountUserId + serviceAccountRole.RealmId = fetchedServiceAccountRole.RealmId + serviceAccountRole.ClientRole = fetchedServiceAccountRole.ClientRole + serviceAccountRole.ContainerId = fetchedServiceAccountRole.ContainerId + serviceAccountRole.Id = fetchedServiceAccountRole.Id + + return nil + } +} + +func testAccCheckKeycloakOpenidClientServiceAccountRoleDestroy() resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "keycloak_openid_client_service_account_role" { + continue + } + + realmId := rs.Primary.Attributes["realm_id"] + serviceAccountUserId := rs.Primary.Attributes["service_account_user_id"] + clientId := rs.Primary.Attributes["client_id"] + id := rs.Primary.ID + + keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient) + + serviceAccountRole, _ := keycloakClient.GetOpenidClientServiceAccountRole(realmId, serviceAccountUserId, clientId, id) + if serviceAccountRole != nil { + return fmt.Errorf("service account role exists") + } + } + + return nil + } +} + +func getKeycloakOpenidClientServiceAccountRoleFromState(s *terraform.State, resourceName string) (*keycloak.OpenidClientServiceAccountRole, error) { + keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient) + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return nil, fmt.Errorf("resource not found: %s", resourceName) + } + + realmId := rs.Primary.Attributes["realm_id"] + serviceAccountUserId := rs.Primary.Attributes["service_account_user_id"] + clientId := rs.Primary.Attributes["client_id"] + id := rs.Primary.ID + + serviceAccountRole, err := keycloakClient.GetOpenidClientServiceAccountRole(realmId, serviceAccountUserId, clientId, id) + if err != nil { + return nil, fmt.Errorf("error getting service account role mapping: %s", err) + } + + return serviceAccountRole, nil +} + +func testKeycloakOpenidClientServiceAccountRole_basic(realm, clientId string) string { + return fmt.Sprintf(` +resource keycloak_realm test { + realm = "%s" +} + +resource keycloak_openid_client test { + client_id = "%s" + realm_id = "${keycloak_realm.test.id}" + access_type = "CONFIDENTIAL" + service_accounts_enabled = true +} + +data keycloak_openid_client broker { + realm_id = "${keycloak_realm.test.id}" + client_id = "broker" +} + +resource keycloak_openid_client_service_account_role test { + service_account_user_id = "${keycloak_openid_client.test.service_account_user_id}" + realm_id = "${keycloak_realm.test.id}" + client_id = "${data.keycloak_openid_client.broker.id}" + role = "read-token" +} + `, realm, clientId) +} diff --git a/provider/resource_keycloak_openid_client_test.go b/provider/resource_keycloak_openid_client_test.go index 889f56ab..1beeb664 100644 --- a/provider/resource_keycloak_openid_client_test.go +++ b/provider/resource_keycloak_openid_client_test.go @@ -129,6 +129,10 @@ func TestAccKeycloakOpenidClient_updateInPlace(t *testing.T) { directAccessGrantsEnabled := randomBool() serviceAccountsEnabled := randomBool() + if !standardFlowEnabled { + implicitFlowEnabled = !standardFlowEnabled + } + openidClientBefore := &keycloak.OpenidClient{ RealmId: realm, ClientId: clientId, @@ -144,6 +148,8 @@ func TestAccKeycloakOpenidClient_updateInPlace(t *testing.T) { WebOrigins: []string{acctest.RandString(10), acctest.RandString(10), acctest.RandString(10)}, } + standardFlowEnabled, implicitFlowEnabled = implicitFlowEnabled, standardFlowEnabled + openidClientAfter := &keycloak.OpenidClient{ RealmId: realm, ClientId: clientId, @@ -151,8 +157,8 @@ func TestAccKeycloakOpenidClient_updateInPlace(t *testing.T) { Enabled: !enabled, Description: acctest.RandString(50), ClientSecret: acctest.RandString(10), - StandardFlowEnabled: !standardFlowEnabled, - ImplicitFlowEnabled: !implicitFlowEnabled, + StandardFlowEnabled: standardFlowEnabled, + ImplicitFlowEnabled: implicitFlowEnabled, DirectAccessGrantsEnabled: !directAccessGrantsEnabled, ServiceAccountsEnabled: !serviceAccountsEnabled, ValidRedirectUris: []string{acctest.RandString(10), acctest.RandString(10)}, diff --git a/provider/resource_keycloak_user.go b/provider/resource_keycloak_user.go index da396b7d..e9db4ed5 100644 --- a/provider/resource_keycloak_user.go +++ b/provider/resource_keycloak_user.go @@ -49,6 +49,30 @@ func resourceKeycloakUser() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "attributes": { + Type: schema.TypeMap, + Optional: true, + }, + "federated_identity": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "identity_provider": { + Type: schema.TypeString, + Required: true, + }, + "user_id": { + Type: schema.TypeString, + Required: true, + }, + "user_name": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, "initial_password": { Type: schema.TypeList, Optional: true, @@ -83,15 +107,44 @@ func onlyDiffOnCreate(_, _, _ string, d *schema.ResourceData) bool { } func mapFromDataToUser(data *schema.ResourceData) *keycloak.User { + attributes := map[string][]string{} + if v, ok := data.GetOk("attributes"); ok { + for key, value := range v.(map[string]interface{}) { + attributes[key] = strings.Split(value.(string), ",") + } + } + + federatedIdentities := &keycloak.FederatedIdentities{} + + if v, ok := data.GetOk("federated_identity"); ok { + federatedIdentities = getUserFederatedIdentitiesFromData(v.(*schema.Set).List()) + } + return &keycloak.User{ - Id: data.Id(), - RealmId: data.Get("realm_id").(string), - Username: data.Get("username").(string), - Email: data.Get("email").(string), - FirstName: data.Get("first_name").(string), - LastName: data.Get("last_name").(string), - Enabled: data.Get("enabled").(bool), + Id: data.Id(), + RealmId: data.Get("realm_id").(string), + Username: data.Get("username").(string), + Email: data.Get("email").(string), + FirstName: data.Get("first_name").(string), + LastName: data.Get("last_name").(string), + Enabled: data.Get("enabled").(bool), + Attributes: attributes, + FederatedIdentities: *federatedIdentities, + } +} + +func getUserFederatedIdentitiesFromData(data []interface{}) *keycloak.FederatedIdentities { + var federatedIdentities keycloak.FederatedIdentities + for _, d := range data { + federatedIdentitiesData := d.(map[string]interface{}) + federatedIdentity := keycloak.FederatedIdentity{ + IdentityProvider: federatedIdentitiesData["display_name"].(string), + UserId: federatedIdentitiesData["name"].(string), + UserName: federatedIdentitiesData["icon_uri"].(string), + } + federatedIdentities = append(federatedIdentities, federatedIdentity) } + return &federatedIdentities } func mapFromUserToData(data *schema.ResourceData, user *keycloak.User) {