Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add Management Permissions, Sync Mode and generic client role mapper. #316

Merged
merged 41 commits into from
Jul 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
c2a38fe
Initial implementation of CRUD operations (no importer yet)
slavko-vega May 13, 2020
0a8b15e
Map resource name with function
slavko-vega May 13, 2020
f106a11
Importer added
slavko-vega May 13, 2020
b771841
Tests support
slavko-vega May 14, 2020
85c768e
Fix path and Id
slavko-vega May 14, 2020
f095332
Fix path and Id
slavko-vega May 14, 2020
dc8b565
Fix the url problem
slavko-vega May 15, 2020
11c5d74
extend resource_keycloak_generic_client_role_mapper to support realm-…
branislav-vega May 15, 2020
c1b1a93
Test fixies
slavko-vega May 15, 2020
baa414d
Basic test
slavko-vega May 16, 2020
7788f36
Streamline the structure, code and tests
slavko-vega May 16, 2020
cadf3fc
remove local files
slavko-vega May 16, 2020
0b42ddc
Set up CI with Azure Pipelines
m-v-k May 19, 2020
fc31223
Update azure-pipelines.yml for Azure Pipelines
m-v-k May 19, 2020
2afe728
add CIRCLE_TAG
m-v-k May 19, 2020
eef89cc
bash inline with variable
m-v-k May 19, 2020
073eca1
add release
m-v-k May 19, 2020
ffdd878
add ability to provide custom configuration to idp mappers
branislav-vega Jun 5, 2020
7299677
extend tests and examples
branislav-vega Jun 8, 2020
0e1c2fd
test fix
branislav-vega Jun 8, 2020
880fa5d
Merge pull request #1 from embracesbs/features/resource-keycloak-gene…
branislav-vega Jun 10, 2020
019fdf8
Merge pull request #2 from embracesbs/features/management-permission-…
slavko-vega Jun 10, 2020
cd94268
Merge pull request #3 from embracesbs/features/idp-mapper-extra-config
branislav-vega Jun 10, 2020
2586bfd
remove embracesbs azure pipeline
Jun 10, 2020
bae0f66
Revert "Extension of keycloak_generic_client_role_mapper resource to …
branislav-vega Jun 10, 2020
138a7f1
Merge pull request #5 from embracesbs/revert-1-features/resource-keyc…
branislav-vega Jun 10, 2020
a83b13b
Merge pull request #4 from mrparkers/master
branislav-vega Jun 10, 2020
8aa881c
Merge pull request #6 from embracesbs/master
m-v-k Jun 10, 2020
53b2291
Revert "Revert "Extension of keycloak_generic_client_role_mapper reso…
branislav-vega Jun 10, 2020
87d405b
Merge pull request #7 from embracesbs/features/resource-keycloak-gene…
branislav-vega Jun 10, 2020
72d490c
Merge pull request #8 from embracesbs/master
m-v-k Jun 11, 2020
c8139f9
test fix
branislav-vega Jun 11, 2020
547eb47
Merge pull request #9 from embracesbs/features/resource-keycloak-gene…
branislav-vega Jun 11, 2020
da03d4d
Merge pull request #10 from embracesbs/master
m-v-k Jun 11, 2020
8aceb20
Merge branch 'master' into upstream
m-v-k Jun 18, 2020
877b2e4
Revert "Merge pull request #2 from embracesbs/features/management-per…
slavko-vega Jul 1, 2020
14d61e5
Use TF 0.12 references format
slavko-vega Jul 1, 2020
7439ddf
Merge pull request #12 from embracesbs/hotfixes/remove-management-per…
slavko-vega Jul 1, 2020
3b2ca21
Merge pull request #13 from embracesbs/hotfixes/remove-management-per…
slavko-vega Jul 2, 2020
2597e88
Merge pull request #14 from embracesbs/master
slavko-vega Jul 2, 2020
1a459d3
Merge branch 'master' into upstream
slavko-vega Jul 2, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions example/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,11 @@ resource keycloak_attribute_importer_identity_provider_mapper oidc {
claim_name = "upn"
identity_provider_alias = keycloak_oidc_identity_provider.oidc.alias
user_attribute = "email"

#KC10 support
extra_config = {
syncMode = "INHERIT"
}
}

resource keycloak_attribute_to_role_identity_provider_mapper oidc {
Expand All @@ -576,20 +581,35 @@ resource keycloak_attribute_to_role_identity_provider_mapper oidc {
identity_provider_alias = keycloak_oidc_identity_provider.oidc.alias
claim_value = "value"
role = "testRole"

#KC10 support
extra_config = {
syncMode = "INHERIT"
}
}

resource keycloak_user_template_importer_identity_provider_mapper oidc {
realm = keycloak_realm.test.id
name = "userTemplate"
identity_provider_alias = keycloak_oidc_identity_provider.oidc.alias
template = "$${ALIAS}/$${CLAIM.upn}"

#KC10 support
extra_config = {
syncMode = "INHERIT"
}
}

resource keycloak_hardcoded_role_identity_provider_mapper oidc {
realm = keycloak_realm.test.id
name = "hardcodedRole"
identity_provider_alias = keycloak_oidc_identity_provider.oidc.alias
role = "testrole"

#KC10 support
extra_config = {
syncMode = "INHERIT"
}
}

resource keycloak_hardcoded_attribute_identity_provider_mapper oidc {
Expand All @@ -599,6 +619,11 @@ resource keycloak_hardcoded_attribute_identity_provider_mapper oidc {
attribute_name = "attribute"
attribute_value = "value"
user_session = true

#KC10 support
extra_config = {
syncMode = "INHERIT"
}
}

resource keycloak_saml_identity_provider saml {
Expand All @@ -613,6 +638,11 @@ resource keycloak_attribute_importer_identity_provider_mapper saml {
attribute_name = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
identity_provider_alias = keycloak_saml_identity_provider.saml.alias
user_attribute = "email"

#KC10 support
extra_config = {
syncMode = "INHERIT"
}
}

resource keycloak_attribute_to_role_identity_provider_mapper saml {
Expand All @@ -622,20 +652,35 @@ resource keycloak_attribute_to_role_identity_provider_mapper saml {
identity_provider_alias = keycloak_saml_identity_provider.saml.alias
attribute_value = "value"
role = "testRole"

#KC10 support
extra_config = {
syncMode = "INHERIT"
}
}

resource keycloak_user_template_importer_identity_provider_mapper saml {
realm = keycloak_realm.test.id
name = "userTemplate"
identity_provider_alias = keycloak_saml_identity_provider.saml.alias
template = "$${ALIAS}/$${NAMEID}"

#KC10 support
extra_config = {
syncMode = "INHERIT"
}
}

resource keycloak_hardcoded_role_identity_provider_mapper saml {
realm = keycloak_realm.test.id
name = "hardcodedRole"
identity_provider_alias = keycloak_saml_identity_provider.saml.alias
role = "testrole"

#KC10 support
extra_config = {
syncMode = "INHERIT"
}
}

resource keycloak_hardcoded_attribute_identity_provider_mapper saml {
Expand All @@ -645,6 +690,11 @@ resource keycloak_hardcoded_attribute_identity_provider_mapper saml {
attribute_name = "attribute"
attribute_value = "value"
user_session = false

#KC10 support
extra_config = {
syncMode = "INHERIT"
}
}

data "keycloak_openid_client" "broker" {
Expand Down
38 changes: 38 additions & 0 deletions example/roles.tf
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,44 @@ resource "keycloak_generic_client_role_mapper" "pet_app_pet_api_admin_role_mappi
role_id = keycloak_role.pet_api_admin.id
}

// Realm roles

resource "keycloak_role" "realm_reader" {
realm_id = keycloak_realm.roles_example.id
name = "realm_reader"
description = "Reader realm role"
}

resource "keycloak_role" "realm_writer" {
realm_id = keycloak_realm.roles_example.id
name = "realm_writer"
description = "Writer realm role"
}

resource "keycloak_role" "realm_admin" {
realm_id = keycloak_realm.roles_example.id
name = "realm_admin"
description = "Admin realm composite role"
composite_roles = [
keycloak_role.realm_reader.id,
keycloak_role.realm_writer.id
]
}

// Client scope for realm roles mapping

resource "keycloak_openid_client_scope" "petstore_api_access_scope" {
realm_id = keycloak_realm.roles_example.id
name = "petstore-api-access"
description = "Optional scope offering additional information for petstore api access"
}

resource "keycloak_generic_client_role_mapper" "petstore_api_access_scope_admin" {
realm_id = keycloak_realm.roles_example.id
client_scope_id = keycloak_openid_client_scope.petstore_api_access_scope.id
role_id = keycloak_role.realm_admin.id
}

// Users and groups

resource "keycloak_group" "pet_api_base" {
Expand Down
77 changes: 68 additions & 9 deletions keycloak/identity_provider_mapper.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
package keycloak

import (
"encoding/json"
"fmt"
"log"
"reflect"
"strconv"
"strings"
)

type IdentityProviderMapperConfig struct {
UserAttribute string `json:"user.attribute,omitempty"`
Claim string `json:"claim,omitempty"`
ClaimValue string `json:"claim.value,omitempty"`
HardcodedAttribute string `json:"attribute,omitempty"`
Attribute string `json:"attribute.name,omitempty"`
AttributeValue string `json:"attribute.value,omitempty"`
AttributeFriendlyName string `json:"attribute.friendly.name,omitempty"`
Template string `json:"template,omitempty"`
Role string `json:"role,omitempty"`
UserAttribute string `json:"user.attribute,omitempty"`
Claim string `json:"claim,omitempty"`
ClaimValue string `json:"claim.value,omitempty"`
HardcodedAttribute string `json:"attribute,omitempty"`
Attribute string `json:"attribute.name,omitempty"`
AttributeValue string `json:"attribute.value,omitempty"`
AttributeFriendlyName string `json:"attribute.friendly.name,omitempty"`
Template string `json:"template,omitempty"`
Role string `json:"role,omitempty"`
ExtraConfig map[string]interface{} `json:"-"`
}

type IdentityProviderMapper struct {
Expand Down Expand Up @@ -59,3 +64,57 @@ 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), nil)
}

func (f *IdentityProviderMapperConfig) UnmarshalJSON(data []byte) error {
f.ExtraConfig = map[string]interface{}{}
err := json.Unmarshal(data, &f.ExtraConfig)
if err != nil {
return err
}
v := reflect.ValueOf(f).Elem()
for i := 0; i < v.NumField(); i++ {
structField := v.Type().Field(i)
jsonKey := strings.Split(structField.Tag.Get("json"), ",")[0]
if jsonKey != "-" {
value, ok := f.ExtraConfig[jsonKey]
if ok {
field := v.FieldByName(structField.Name)
if field.IsValid() && field.CanSet() {
if field.Kind() == reflect.String {
field.SetString(value.(string))
} else if field.Kind() == reflect.Bool {
boolVal, err := strconv.ParseBool(value.(string))
if err == nil {
field.Set(reflect.ValueOf(KeycloakBoolQuoted(boolVal)))
}
}
delete(f.ExtraConfig, jsonKey)
}
}
}
}
return nil
}

func (f *IdentityProviderMapperConfig) MarshalJSON() ([]byte, error) {
out := map[string]interface{}{}

for k, v := range f.ExtraConfig {
out[k] = v
}
v := reflect.ValueOf(f).Elem()
for i := 0; i < v.NumField(); i++ {
jsonKey := strings.Split(v.Type().Field(i).Tag.Get("json"), ",")[0]
if jsonKey != "-" {
field := v.Field(i)
if field.IsValid() && field.CanSet() {
if field.Kind() == reflect.String {
out[jsonKey] = field.String()
} else if field.Kind() == reflect.Bool {
out[jsonKey] = KeycloakBoolQuoted(field.Bool())
}
}
}
}
return json.Marshal(out)
}
13 changes: 11 additions & 2 deletions keycloak/role_scope_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,19 @@ import (
)

func roleScopeMappingUrl(realmId, clientId string, clientScopeId string, role *Role) string {

if clientId != "" {
return fmt.Sprintf("/realms/%s/clients/%s/scope-mappings/clients/%s", realmId, clientId, role.ClientId)
} else {
if role.ClientRole {
return fmt.Sprintf("/realms/%s/clients/%s/scope-mappings/clients/%s", realmId, clientId, role.ClientId)
} else {
return fmt.Sprintf("/realms/%s/clients/%s/scope-mappings/realm", realmId, clientId)
}
}

if role.ClientRole {
return fmt.Sprintf("/realms/%s/client-scopes/%s/scope-mappings/clients/%s", realmId, clientScopeId, role.ClientId)
} else {
return fmt.Sprintf("/realms/%s/client-scopes/%s/scope-mappings/realm", realmId, clientScopeId)
}
}

Expand Down
7 changes: 6 additions & 1 deletion provider/generic_keycloak_identity_provider_mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package provider

import (
"fmt"
"strings"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/mrparkers/terraform-provider-keycloak/keycloak"
"strings"
)

type identityProviderMapperDataGetterFunc func(data *schema.ResourceData, meta interface{}) (*keycloak.IdentityProviderMapper, error)
Expand Down Expand Up @@ -35,6 +36,10 @@ func resourceKeycloakIdentityProviderMapper() *schema.Resource {
ForceNew: true,
Description: "IDP Alias",
},
"extra_config": {
Type: schema.TypeMap,
Optional: true,
},
},
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package provider

import (
"fmt"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/mrparkers/terraform-provider-keycloak/keycloak"
)
Expand Down Expand Up @@ -42,13 +43,20 @@ func resourceKeycloakAttributeImporterIdentityProviderMapper() *schema.Resource
func getAttributeImporterIdentityProviderMapperFromData(data *schema.ResourceData, meta interface{}) (*keycloak.IdentityProviderMapper, error) {
keycloakClient := meta.(*keycloak.KeycloakClient)
rec, _ := getIdentityProviderMapperFromData(data)
extraConfig := map[string]interface{}{}
if v, ok := data.GetOk("extra_config"); ok {
for key, value := range v.(map[string]interface{}) {
extraConfig[key] = value
}
}
identityProvider, err := keycloakClient.GetIdentityProvider(rec.Realm, rec.IdentityProviderAlias)
if err != nil {
return nil, handleNotFoundError(err, data)
}
rec.IdentityProviderMapper = fmt.Sprintf("%s-user-attribute-idp-mapper", identityProvider.ProviderId)
rec.Config = &keycloak.IdentityProviderMapperConfig{
UserAttribute: data.Get("user_attribute").(string),
ExtraConfig: extraConfig,
}
if identityProvider.ProviderId == "saml" {
if attr, ok := data.GetOk("attribute_friendly_name"); ok {
Expand All @@ -75,5 +83,6 @@ func setAttributeImporterIdentityProviderMapperData(data *schema.ResourceData, i
data.Set("user_attribute", identityProviderMapper.Config.UserAttribute)
data.Set("attribute_friendly_name", identityProviderMapper.Config.AttributeFriendlyName)
data.Set("claim_name", identityProviderMapper.Config.Claim)
data.Set("extra_config", identityProviderMapper.Config.ExtraConfig)
return nil
}
Loading