Repository: mrparkers/terraform-provider-keycloak
Pull request #438 (2020-11-18 14:43:14)

Validation of RegistrationEmailAsUsername attribute
Background:
I want to have a realm, where `username` and `email` attributes have same value, registration is disabled and user is allowed to manage own email address. To achieve this configuration keycloak can be configured as follows

```
RegistrationAllowed=false
RegistrationEmailAsUsername=true
EditUsernameAllowed=false
```

As a result, keycloak's account management page will allow user to manage own email address (but not username), and username will be updated automatically by keycloak upon change.

This configuration is of course not very intuitive, because `RegistrationEmailAsUsername` attribute _should_ make sense only when registration is enabled, but in fact keycloak behaves just as described above. In order to achieve this from GUI, `RegistrationAllowed` can be temporary enabled / disabled, which will allow setting `RegistrationEmailAsUsername` to true. 

Current implementation of the provider does not allow setting `RegistrationEmailAsUsername` to true when `RegistrationAllowed` is false, resulting in `validation error: RegistrationEmailAsUsername cannot be true if RegistrationAllowed is false`. This change removes that condition.


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index dd77b4f..6765d10 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -6,13 +6,13 @@ workflows:
           matrix:
             parameters:
               keycloak-version:
-                - '11.0.1'
+                - '11.0.3'
                 - '10.0.2'
                 - '9.0.3'
 
 defaults:
   go_image: &go_image
-    - image: circleci/golang:1.13.5
+    - image: circleci/golang:1.15.5
 
 jobs:
   test:
@@ -22,7 +22,7 @@ jobs:
     docker:
       - <<: *go_image
       - image: jboss/keycloak:<< parameters.keycloak-version >>
-        command: ["-b", "0.0.0.0", "-Dkeycloak.profile.feature.upload_scripts=enabled"]
+        command: ["-b", "0.0.0.0", "-Dkeycloak.profile.feature.upload_scripts=enabled", "-Dkeycloak.profile.feature.admin_fine_grained_authz=enabled", "-Dkeycloak.profile.feature.token_exchange=enabled"]
         environment:
           DB_VENDOR: H2
           KEYCLOAK_LOGLEVEL: DEBUG
@@ -74,4 +74,4 @@ jobs:
       TEST_RESULTS: /tmp/test-results
       CHECKPOINT_DISABLE: "1"
       TF_ACC_TERRAFORM_PATH: "/usr/local/bin/terraform"
-      TF_VERSION: "0.13.0"
+      TF_VERSION: "0.13.5"
diff --git a/README.md b/README.md
index b1ca78d..4f8b4d2 100644
--- a/README.md
+++ b/README.md
@@ -59,7 +59,7 @@ build you can use the `linux_amd64` build as long as `libc6-compat` is installed
 
 ## Development
 
-This project requires Go 1.13 and Terraform 0.13.
+This project requires Go 1.15 and Terraform 0.13.
 This project uses [Go Modules](https://github.com/golang/go/wiki/Modules) for dependency management, which allows this project to exist outside of an existing GOPATH.
 
 After cloning the repository, you can build the project by running `make build`.
diff --git a/docker-compose.yml b/docker-compose.yml
index 7088737..e2103f2 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -17,7 +17,7 @@ services:
     ports:
     - 8389:389
   keycloak:
-    image:  jboss/keycloak:10.0.2
+    image:  jboss/keycloak:11.0.3
     command: -b 0.0.0.0 -Dkeycloak.profile.feature.upload_scripts=enabled -Dkeycloak.profile.feature.admin_fine_grained_authz=enabled -Dkeycloak.profile.feature.token_exchange=enabled
     depends_on:
     - postgres
diff --git a/go.mod b/go.mod
index f706892..2f70de9 100644
--- a/go.mod
+++ b/go.mod
@@ -7,4 +7,4 @@ require (
 	golang.org/x/net v0.0.0-20200707034311-ab3426394381
 )
 
-go 1.13
+go 1.15
diff --git a/keycloak/keycloak_client.go b/keycloak/keycloak_client.go
index d914e72..6a102dc 100644
--- a/keycloak/keycloak_client.go
+++ b/keycloak/keycloak_client.go
@@ -6,6 +6,7 @@ import (
 	"crypto/x509"
 	"encoding/json"
 	"fmt"
+	"github.com/hashicorp/go-version"
 	"io"
 	"io/ioutil"
 	"log"
@@ -26,6 +27,7 @@ type KeycloakClient struct {
 	httpClient        *http.Client
 	initialLogin      bool
 	userAgent         string
+	version           *version.Version
 }
 
 type ClientCredentials struct {
@@ -138,7 +140,6 @@ func (keycloakClient *KeycloakClient) login() error {
 
 	var clientCredentials ClientCredentials
 	err = json.Unmarshal(body, &clientCredentials)
-
 	if err != nil {
 		return err
 	}
@@ -147,6 +148,18 @@ func (keycloakClient *KeycloakClient) login() error {
 	keycloakClient.clientCredentials.RefreshToken = clientCredentials.RefreshToken
 	keycloakClient.clientCredentials.TokenType = clientCredentials.TokenType
 
+	info, err := keycloakClient.GetServerInfo()
+	if err != nil {
+		return err
+	}
+
+	v, err := version.NewVersion(info.SystemInfo.ServerVersion)
+	if err != nil {
+		return err
+	}
+
+	keycloakClient.version = v
+
 	return nil
 }
 
diff --git a/keycloak/realm.go b/keycloak/realm.go
index f13d7f1..52658b3 100644
--- a/keycloak/realm.go
+++ b/keycloak/realm.go
@@ -199,10 +199,6 @@ func (keycloakClient *KeycloakClient) DeleteRealm(name string) error {
 }
 
 func (keycloakClient *KeycloakClient) ValidateRealm(realm *Realm) error {
-	if realm.RegistrationAllowed == false && realm.RegistrationEmailAsUsername == true {
-		return fmt.Errorf("validation error: RegistrationEmailAsUsername cannot be true if RegistrationAllowed is false")
-	}
-
 	if realm.DuplicateEmailsAllowed == true && realm.RegistrationEmailAsUsername == true {
 		return fmt.Errorf("validation error: DuplicateEmailsAllowed cannot be true if RegistrationEmailAsUsername is true")
 	}
diff --git a/keycloak/version.go b/keycloak/version.go
new file mode 100644
index 0000000..2792786
--- /dev/null
+++ b/keycloak/version.go
@@ -0,0 +1,20 @@
+package keycloak
+
+import "github.com/hashicorp/go-version"
+
+type Version string
+
+const (
+	Version_6  Version = "6.0.0"
+	Version_7  Version = "7.0.0"
+	Version_8  Version = "8.0.0"
+	Version_9  Version = "9.0.0"
+	Version_10 Version = "10.0.0"
+	Version_11 Version = "11.0.0"
+)
+
+func (keycloakClient *KeycloakClient) VersionIsGreaterThanOrEqualTo(versionString Version) bool {
+	v, _ := version.NewVersion(string(versionString))
+
+	return keycloakClient.version.GreaterThanOrEqual(v)
+}
diff --git a/provider/resource_keycloak_openid_client.go b/provider/resource_keycloak_openid_client.go
index d62d628..6cfbefd 100644
--- a/provider/resource_keycloak_openid_client.go
+++ b/provider/resource_keycloak_openid_client.go
@@ -1,10 +1,12 @@
 package provider
 
 import (
+	"context"
 	"errors"
 	"fmt"
 	"strings"
 
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
 	"github.com/mrparkers/terraform-provider-keycloak/keycloak"
@@ -205,6 +207,9 @@ func resourceKeycloakOpenidClient() *schema.Resource {
 				Optional: true,
 			},
 		},
+		CustomizeDiff: customdiff.ComputedIf("service_account_user_id", func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) bool {
+			return d.HasChange("service_accounts_enabled")
+		}),
 	}
 }
 
diff --git a/provider/resource_keycloak_openid_client_service_account_realm_role.go b/provider/resource_keycloak_openid_client_service_account_realm_role.go
index e20204c..7f8a16e 100644
--- a/provider/resource_keycloak_openid_client_service_account_realm_role.go
+++ b/provider/resource_keycloak_openid_client_service_account_realm_role.go
@@ -112,14 +112,27 @@ func resourceKeycloakOpenidClientServiceAccountRealmRoleDelete(data *schema.Reso
 	return nil
 }
 
-func resourceKeycloakOpenidClientServiceAccountRealmRoleImport(d *schema.ResourceData, _ interface{}) ([]*schema.ResourceData, error) {
+func resourceKeycloakOpenidClientServiceAccountRealmRoleImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
+	keycloakClient := meta.(*keycloak.KeycloakClient)
+
 	parts := strings.Split(d.Id(), "/")
-	if len(parts) != 2 {
+	if len(parts) != 3 {
 		return nil, fmt.Errorf("Invalid import. Supported import formats: {{realmId}}/{{serviceAccountUserId}}/{{roleId}}")
 	}
-	d.Set("realm_id", parts[0])
-	d.Set("service_account_user_id", parts[1])
-	d.SetId(fmt.Sprintf("%s/%s", parts[1], parts[2]))
+
+	realmId := parts[0]
+	serviceAccountUserId := parts[1]
+	roleId := parts[2]
+
+	role, err := keycloakClient.GetRole(realmId, roleId)
+	if err != nil {
+		return nil, err
+	}
+
+	d.Set("realm_id", realmId)
+	d.Set("service_account_user_id", serviceAccountUserId)
+	d.Set("role", role.Name)
+	d.SetId(fmt.Sprintf("%s/%s", serviceAccountUserId, roleId))
 
 	return []*schema.ResourceData{d}, nil
 }


Test patch:
diff --git a/provider/resource_keycloak_openid_client_default_scopes_test.go b/provider/resource_keycloak_openid_client_default_scopes_test.go
index 3a25f15..38ea201 100644
--- a/provider/resource_keycloak_openid_client_default_scopes_test.go
+++ b/provider/resource_keycloak_openid_client_default_scopes_test.go
@@ -293,7 +293,7 @@ func TestAccKeycloakOpenidClientDefaultScopes_validateDuplicateScopeAssignment(t
 	client := "terraform-client-" + acctest.RandString(10)
 	clientScope := "terraform-client-scope-" + acctest.RandString(10)
 
-	optionalClientScopes := append(getPreAssignedOptionalClientScopes(t), clientScope)
+	optionalClientScopes := append(getPreAssignedOptionalClientScopes(), clientScope)
 
 	resource.Test(t, resource.TestCase{
 		ProviderFactories: testAccProviderFactories,
diff --git a/provider/resource_keycloak_openid_client_optional_scopes_test.go b/provider/resource_keycloak_openid_client_optional_scopes_test.go
index 12d8851..1451d36 100644
--- a/provider/resource_keycloak_openid_client_optional_scopes_test.go
+++ b/provider/resource_keycloak_openid_client_optional_scopes_test.go
@@ -12,12 +12,8 @@ import (
 )
 
 // All openid clients in Keycloak will automatically have these scopes listed as "optional client scopes".
-func getPreAssignedOptionalClientScopes(t *testing.T) []string {
-	keycloakVersionIsGreaterThanOrEqualTo6, err := keycloakVersionIsGreaterThanOrEqualTo(keycloakClient, getKeycloakVersion600())
-	if err != nil {
-		t.Fatal(err)
-	}
-	if keycloakVersionIsGreaterThanOrEqualTo6 {
+func getPreAssignedOptionalClientScopes() []string {
+	if keycloakClient.VersionIsGreaterThanOrEqualTo(keycloak.Version_6) {
 		return []string{"address", "phone", "offline_access", "microprofile-jwt"}
 	} else {
 		return []string{"address", "phone", "offline_access"}
@@ -29,7 +25,7 @@ func TestAccKeycloakOpenidClientOptionalScopes_basic(t *testing.T) {
 	client := "terraform-client-" + acctest.RandString(10)
 	clientScope := "terraform-client-scope-" + acctest.RandString(10)
 
-	clientScopes := append(getPreAssignedOptionalClientScopes(t), clientScope)
+	clientScopes := append(getPreAssignedOptionalClientScopes(), clientScope)
 
 	resource.Test(t, resource.TestCase{
 		ProviderFactories: testAccProviderFactories,
@@ -55,7 +51,7 @@ func TestAccKeycloakOpenidClientOptionalScopes_updateClientForceNew(t *testing.T
 	clientTwo := "terraform-client-" + acctest.RandString(10)
 	clientScope := "terraform-client-scope-" + acctest.RandString(10)
 
-	clientScopes := append(getPreAssignedOptionalClientScopes(t), clientScope)
+	clientScopes := append(getPreAssignedOptionalClientScopes(), clientScope)
 
 	resource.Test(t, resource.TestCase{
 		ProviderFactories: testAccProviderFactories,
@@ -78,7 +74,7 @@ func TestAccKeycloakOpenidClientOptionalScopes_updateInPlace(t *testing.T) {
 	client := "terraform-client-" + acctest.RandString(10)
 	clientScope := "terraform-client-scope-" + acctest.RandString(10)
 
-	allClientScopes := append(getPreAssignedOptionalClientScopes(t), clientScope)
+	allClientScopes := append(getPreAssignedOptionalClientScopes(), clientScope)
 
 	clientScopeToRemove := allClientScopes[acctest.RandIntRange(0, 2)]
 	var subsetOfClientScopes []string
@@ -149,7 +145,7 @@ func TestAccKeycloakOpenidClientOptionalScopes_validateClientAccessType(t *testi
 func TestAccKeycloakOpenidClientOptionalScopes_authoritativeAdd(t *testing.T) {
 	realm := "terraform-realm-" + acctest.RandString(10)
 	client := "terraform-client-" + acctest.RandString(10)
-	clientScopes := append(getPreAssignedOptionalClientScopes(t),
+	clientScopes := append(getPreAssignedOptionalClientScopes(),
 		"terraform-client-scope-"+acctest.RandString(10),
 		"terraform-client-scope-"+acctest.RandString(10),
 		"terraform-client-scope-"+acctest.RandString(10),
@@ -195,7 +191,7 @@ func TestAccKeycloakOpenidClientOptionalScopes_authoritativeRemove(t *testing.T)
 		"terraform-client-scope-" + acctest.RandString(10),
 		"terraform-client-scope-" + acctest.RandString(10),
 	}
-	allClientScopes := append(getPreAssignedOptionalClientScopes(t), randomClientScopes...)
+	allClientScopes := append(getPreAssignedOptionalClientScopes(), randomClientScopes...)
 
 	clientToManuallyAttach := randomClientScopes[acctest.RandIntRange(0, len(randomClientScopes)-1)]
 	var attachedClientScopes []string
@@ -243,7 +239,7 @@ func TestAccKeycloakOpenidClientOptionalScopes_noImportNeeded(t *testing.T) {
 	client := "terraform-client-" + acctest.RandString(10)
 	clientScope := "terraform-client-scope-" + acctest.RandString(10)
 
-	clientScopes := append(getPreAssignedOptionalClientScopes(t), clientScope)
+	clientScopes := append(getPreAssignedOptionalClientScopes(), clientScope)
 
 	resource.Test(t, resource.TestCase{
 		ProviderFactories: testAccProviderFactories,
@@ -291,7 +287,7 @@ func TestAccKeycloakOpenidClientOptionalScopes_profileAndEmailOptionalScopes(t *
 		Steps: []resource.TestStep{
 			{
 				Config:             testKeycloakOpenidClientOptionalScopes_listOfScopes(realm, client, clientScope, []string{clientScope}),
-				Check:              testAccCheckKeycloakOpenidClientHasOptionalScopes("keycloak_openid_client.client", append(getPreAssignedOptionalClientScopes(t), clientScope)),
+				Check:              testAccCheckKeycloakOpenidClientHasOptionalScopes("keycloak_openid_client.client", append(getPreAssignedOptionalClientScopes(), clientScope)),
 				ExpectNonEmptyPlan: true,
 			},
 		},
@@ -409,8 +405,7 @@ func testAccCheckKeycloakOpenidClientOptionalScopeIsNotAttached(resourceName, cl
 }
 
 func testKeycloakOpenidClientOptionalScopes_basic(realm, client, clientScope string) string {
-	keycloakVersionIsHigherOrEqualTo6, _ := keycloakVersionIsGreaterThanOrEqualTo(keycloakClient, getKeycloakVersion600())
-	if keycloakVersionIsHigherOrEqualTo6 {
+	if keycloakClient.VersionIsGreaterThanOrEqualTo(keycloak.Version_6) {
 		return fmt.Sprintf(`
 resource "keycloak_realm" "realm" {
 	realm = "%s"
@@ -525,8 +520,7 @@ resource "keycloak_openid_client_optional_scopes" "optional_scopes" {
 }
 
 func testKeycloakOpenidClientOptionalScopes_validationNoClient(realm, client, clientScope string) string {
-	keycloakVersionIsHigherOrEqualTo6, _ := keycloakVersionIsGreaterThanOrEqualTo(keycloakClient, getKeycloakVersion600())
-	if keycloakVersionIsHigherOrEqualTo6 {
+	if keycloakClient.VersionIsGreaterThanOrEqualTo(keycloak.Version_6) {
 		return fmt.Sprintf(`
 resource "keycloak_realm" "realm" {
 	realm = "%s"
@@ -579,9 +573,7 @@ resource "keycloak_openid_client_optional_scopes" "optional_scopes" {
 }
 
 func testKeycloakOpenidClientOptionalScopes_validationBearerOnlyClient(realm, client, clientScope string) string {
-
-	keycloakVersionIsHigherOrEqualTo6, _ := keycloakVersionIsGreaterThanOrEqualTo(keycloakClient, getKeycloakVersion600())
-	if keycloakVersionIsHigherOrEqualTo6 {
+	if keycloakClient.VersionIsGreaterThanOrEqualTo(keycloak.Version_6) {
 		return fmt.Sprintf(`
 resource "keycloak_realm" "realm" {
 	realm = "%s"
@@ -689,8 +681,7 @@ resource "keycloak_openid_client_optional_scopes" "optional_scopes" {
 }
 
 func testKeycloakOpenidClientOptionalScopes_duplicateScopeAssignment(realm, client, clientScope string) string {
-	keycloakVersionIsHigherOrEqualTo6, _ := keycloakVersionIsGreaterThanOrEqualTo(keycloakClient, getKeycloakVersion600())
-	if keycloakVersionIsHigherOrEqualTo6 {
+	if keycloakClient.VersionIsGreaterThanOrEqualTo(keycloak.Version_6) {
 		return fmt.Sprintf(`
 %s
 
diff --git a/provider/resource_keycloak_openid_client_service_account_realm_role_test.go b/provider/resource_keycloak_openid_client_service_account_realm_role_test.go
index 9ed8c53..34e4a4d 100644
--- a/provider/resource_keycloak_openid_client_service_account_realm_role_test.go
+++ b/provider/resource_keycloak_openid_client_service_account_realm_role_test.go
@@ -13,6 +13,7 @@ import (
 func TestAccKeycloakOpenidClientServiceAccountRealmRole_basic(t *testing.T) {
 	realmName := "terraform-" + acctest.RandString(10)
 	clientId := "terraform-" + acctest.RandString(10)
+	resourceName := "keycloak_openid_client_service_account_realm_role.test"
 
 	resource.Test(t, resource.TestCase{
 		ProviderFactories: testAccProviderFactories,
@@ -21,7 +22,13 @@ func TestAccKeycloakOpenidClientServiceAccountRealmRole_basic(t *testing.T) {
 		Steps: []resource.TestStep{
 			{
 				Config: testKeycloakOpenidClientServiceAccountRealmRole_basic(realmName, clientId),
-				Check:  testAccCheckKeycloakOpenidClientServiceAccountRealmRoleExists("keycloak_openid_client_service_account_realm_role.test"),
+				Check:  testAccCheckKeycloakOpenidClientServiceAccountRealmRoleExists(resourceName),
+			},
+			{
+				ResourceName:      resourceName,
+				ImportState:       true,
+				ImportStateVerify: true,
+				ImportStateIdFunc: getKeycloakOpenidClientServiceAccountRealmRoleImportId(resourceName),
 			},
 		},
 	})
@@ -155,6 +162,17 @@ func getKeycloakOpenidClientServiceAccountRealmRoleFromState(s *terraform.State,
 	return serviceAccountRole, nil
 }
 
+func getKeycloakOpenidClientServiceAccountRealmRoleImportId(resourceName string) resource.ImportStateIdFunc {
+	return func(s *terraform.State) (string, error) {
+		serviceAccountRole, err := getKeycloakOpenidClientServiceAccountRealmRoleFromState(s, resourceName)
+		if err != nil {
+			return "", err
+		}
+
+		return fmt.Sprintf("%s/%s/%s", serviceAccountRole.RealmId, serviceAccountRole.ServiceAccountUserId, serviceAccountRole.Id), nil
+	}
+}
+
 func testKeycloakOpenidClientServiceAccountRealmRole_basic(realm, clientId string) string {
 	return fmt.Sprintf(`
 resource keycloak_realm test {
diff --git a/provider/resource_keycloak_openid_client_service_account_role_test.go b/provider/resource_keycloak_openid_client_service_account_role_test.go
index 1f3a5c5..77e7e4a 100644
--- a/provider/resource_keycloak_openid_client_service_account_role_test.go
+++ b/provider/resource_keycloak_openid_client_service_account_role_test.go
@@ -93,6 +93,28 @@ func TestAccKeycloakOpenidClientServiceAccountRole_basicUpdateRealm(t *testing.T
 	})
 }
 
+func TestAccKeycloakOpenidClientServiceAccountRole_enableAfterCreate(t *testing.T) {
+	realmName := "terraform-" + acctest.RandString(10)
+	bearerClientId := "terraform-" + acctest.RandString(10)
+	consumerClientId := "terraform-" + acctest.RandString(10)
+	resourceName := "keycloak_openid_client_service_account_role.consumer_service_account_role"
+
+	resource.Test(t, resource.TestCase{
+		ProviderFactories: testAccProviderFactories,
+		PreCheck:          func() { testAccPreCheck(t) },
+		CheckDestroy:      testAccCheckKeycloakOpenidClientServiceAccountRoleDestroy(),
+		Steps: []resource.TestStep{
+			{
+				Config: testKeycloakOpenidClientServiceAccountRole_enableAfterCreate_before(realmName, bearerClientId, consumerClientId),
+			},
+			{
+				Config: testKeycloakOpenidClientServiceAccountRole_enableAfterCreate_after(realmName, bearerClientId, consumerClientId),
+				Check:  testAccCheckKeycloakOpenidClientServiceAccountRoleExists(resourceName),
+			},
+		},
+	})
+}
+
 func testAccCheckKeycloakOpenidClientServiceAccountRoleExists(resourceName string) resource.TestCheckFunc {
 	return func(s *terraform.State) error {
 		_, err := getKeycloakOpenidClientServiceAccountRoleFromState(s, resourceName)
@@ -184,27 +206,90 @@ func getKeycloakOpenidClientServiceAccountRoleImportId(resourceName string) reso
 
 func testKeycloakOpenidClientServiceAccountRole_basic(realm, clientId string) string {
 	return fmt.Sprintf(`
-resource keycloak_realm test {
+resource "keycloak_realm" "test" {
 	realm = "%s"
 }
 
-resource keycloak_openid_client test {
+resource "keycloak_openid_client" "test" {
 	client_id                = "%s"
-	realm_id                 = "${keycloak_realm.test.id}"
+	realm_id                 = keycloak_realm.test.id
 	access_type              = "CONFIDENTIAL"
 	service_accounts_enabled = true
 }
 
-data keycloak_openid_client broker {
-  realm_id  = "${keycloak_realm.test.id}"
+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"
+resource "keycloak_openid_client_service_account_role" "test" {
+	realm_id                = keycloak_realm.test.id
+	client_id               = data.keycloak_openid_client.broker.id
+	service_account_user_id = keycloak_openid_client.test.service_account_user_id
+	role                    = "read-token"
 }
 	`, realm, clientId)
 }
+
+func testKeycloakOpenidClientServiceAccountRole_enableAfterCreate_before(realm, bearerClientId, consumerClientId string) string {
+	return fmt.Sprintf(`
+resource "keycloak_realm" "test" {
+	realm = "%s"
+}
+
+resource "keycloak_openid_client" "bearer" {
+	client_id   = "%s"
+	realm_id    = keycloak_realm.test.id
+	access_type = "BEARER-ONLY"
+}
+
+resource "keycloak_role" "bearer_role" {
+	realm_id  = keycloak_realm.test.id
+	client_id = keycloak_openid_client.bearer.id
+	name      = "bearer-role"
+}
+
+resource "keycloak_openid_client" "consumer" {
+  realm_id  = keycloak_realm.test.id
+  client_id = "%s"
+
+  access_type              = "CONFIDENTIAL"
+  service_accounts_enabled = false
+}
+	`, realm, bearerClientId, consumerClientId)
+}
+
+func testKeycloakOpenidClientServiceAccountRole_enableAfterCreate_after(realm, bearerClientId, consumerClientId string) string {
+	return fmt.Sprintf(`
+resource "keycloak_realm" "test" {
+	realm = "%s"
+}
+
+resource "keycloak_openid_client" "bearer" {
+	client_id   = "%s"
+	realm_id    = keycloak_realm.test.id
+	access_type = "BEARER-ONLY"
+}
+
+resource "keycloak_role" "bearer_role" {
+	realm_id  = keycloak_realm.test.id
+	client_id = keycloak_openid_client.bearer.id
+	name      = "bearer-role"
+}
+
+resource "keycloak_openid_client" "consumer" {
+  realm_id  = keycloak_realm.test.id
+  client_id = "%s"
+
+  access_type              = "CONFIDENTIAL"
+  service_accounts_enabled = true
+}
+
+resource "keycloak_openid_client_service_account_role" "consumer_service_account_role" {
+  realm_id                = keycloak_realm.test.id
+  service_account_user_id = keycloak_openid_client.consumer.service_account_user_id
+  client_id               = keycloak_openid_client.bearer.id
+  role                    = keycloak_role.bearer_role.name
+}
+	`, realm, bearerClientId, consumerClientId)
+}
diff --git a/provider/resource_keycloak_realm_events_test.go b/provider/resource_keycloak_realm_events_test.go
index 2f5f65f..fd26836 100644
--- a/provider/resource_keycloak_realm_events_test.go
+++ b/provider/resource_keycloak_realm_events_test.go
@@ -164,13 +164,9 @@ func TestAccKeycloakRealmEvents_unsetEnabledEventTypes(t *testing.T) {
 						if err != nil {
 							return err
 						}
-						keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient)
-						keycloakVersionIsGreaterThanOrEqualTo7, err := keycloakVersionIsGreaterThanOrEqualTo(keycloakClient, getKeycloakVersion700())
-						if err != nil {
-							return err
-						}
 
-						if keycloakVersionIsGreaterThanOrEqualTo7 { //keycloak versions < 7.0.0 have 63 events, versions >=7.0.0 have 67 events
+						keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient)
+						if keycloakClient.VersionIsGreaterThanOrEqualTo(keycloak.Version_7) { //keycloak versions < 7.0.0 have 63 events, versions >=7.0.0 have 67 events
 							if len(realmEventsConfig.EnabledEventTypes) != 67 {
 								return fmt.Errorf("exptected to enabled_event_types to contain all(67) event types, but it contains %d", len(realmEventsConfig.EnabledEventTypes))
 							}
diff --git a/provider/resource_keycloak_realm_test.go b/provider/resource_keycloak_realm_test.go
index 2314537..3a8d533 100644
--- a/provider/resource_keycloak_realm_test.go
+++ b/provider/resource_keycloak_realm_test.go
@@ -332,10 +332,6 @@ func TestAccKeycloakRealm_loginConfigValidation(t *testing.T) {
 		PreCheck:          func() { testAccPreCheck(t) },
 		CheckDestroy:      testAccCheckKeycloakRealmDestroy(),
 		Steps: []resource.TestStep{
-			{
-				Config:      testKeycloakRealm_invalidRegistrationEmailAsUsernameWithoutRegistrationAllowed(realmName),
-				ExpectError: regexp.MustCompile("validation error: RegistrationEmailAsUsername cannot be true if RegistrationAllowed is false"),
-			},
 			{
 				Config:      testKeycloakRealm_invalidRegistrationEmailAsUsernameAndDuplicateEmailsAllowed(realmName),
 				ExpectError: regexp.MustCompile("validation error: DuplicateEmailsAllowed cannot be true if RegistrationEmailAsUsername is true"),
@@ -1227,17 +1223,6 @@ resource "keycloak_realm" "realm" {
 	`, realm.Realm, realm.RegistrationAllowed, realm.RegistrationEmailAsUsername, realm.EditUsernameAllowed, realm.ResetPasswordAllowed, realm.RememberMe, realm.VerifyEmail, realm.LoginWithEmailAllowed, realm.DuplicateEmailsAllowed, realm.SslRequired)
 }
 
-func testKeycloakRealm_invalidRegistrationEmailAsUsernameWithoutRegistrationAllowed(realm string) string {
-	return fmt.Sprintf(`
-resource "keycloak_realm" "realm" {
-	realm                          = "%s"
-
-	registration_allowed           = false
-	registration_email_as_username = true
-}
-	`, realm)
-}
-
 func testKeycloakRealm_invalidRegistrationEmailAsUsernameAndDuplicateEmailsAllowed(realm string) string {
 	return fmt.Sprintf(`
 resource "keycloak_realm" "realm" {
diff --git a/provider/test_utils.go b/provider/test_utils.go
index d54480b..90924b2 100644
--- a/provider/test_utils.go
+++ b/provider/test_utils.go
@@ -2,11 +2,8 @@ package provider
 
 import (
 	"fmt"
-	"github.com/hashicorp/go-version"
-	"github.com/mrparkers/terraform-provider-keycloak/keycloak"
 	"math/rand"
 	"os"
-	"regexp"
 	"strings"
 	"testing"
 	"time"
@@ -78,48 +75,3 @@ func TestCheckResourceAttrNot(name, key, value string) resource.TestCheckFunc {
 		return nil
 	}
 }
-
-var keycloakServerInfoVersion *version.Version
-
-func keycloakVersionIsGreaterThanOrEqualTo(keycloakClient *keycloak.KeycloakClient, keycloakMajorVersion *version.Version) (bool, error) {
-	if keycloakServerInfoVersion == nil {
-		serverInfo, err := keycloakClient.GetServerInfo()
-		if err != nil {
-			return false, fmt.Errorf("/serverInfo endpoint retuned an error, server Keycloak version could not be determined: %s", err)
-		}
-
-		regex := regexp.MustCompile(`^(\d+\.\d+\.\d+)`)
-		semver := regex.FindStringSubmatch(serverInfo.SystemInfo.ServerVersion)[0]
-
-		keycloakServerInfoVersion, err = version.NewVersion(semver)
-		if err != nil {
-			return false, fmt.Errorf("/serverInfo endpoint retuned an unreadable version, server Keycloak version could not be determined: %s", err)
-		}
-	}
-	return keycloakServerInfoVersion.GreaterThanOrEqual(keycloakMajorVersion), nil
-}
-
-func getKeycloakVersion600() *version.Version {
-	v, _ := version.NewVersion("6.0.0")
-	return v
-}
-
-func getKeycloakVersion700() *version.Version {
-	v, _ := version.NewVersion("7.0.0")
-	return v
-}
-
-func getKeycloakVersion800() *version.Version {
-	v, _ := version.NewVersion("8.0.0")
-	return v
-}
-
-func getKeycloakVersion900() *version.Version {
-	v, _ := version.NewVersion("9.0.0")
-	return v
-}
-
-func getKeycloakVersion1000() *version.Version {
-	v, _ := version.NewVersion("10.0.0")
-	return v
-}