Repository: mrparkers/terraform-provider-keycloak
Pull request #332 (2020-06-30 17:22:41)

Improve provider connection: allow ommited "/auth" and improve login-error-messages
Hey,

Strictly speaking this PR addresses two distinct issues, but both are rather small.

First, the provider previously did not check the status-code of the initial login, so if some reverse-proxy returns an error and a non-JSON-error-message, the error-message is just about a parsing failure. By explicitly checking the HTTP-status-code, the provider can show more precise error messages.

The second commit enables to use keycloak at another web-context then "/auth" (e.g. to leave out "/auth" completely). 
Maybe it would be nicer to just use the setting `baseUrl` and require users to include "/auth" if necessary, but that would introduce a breaking change, so I opted to add an additional optional setting to override the "/auth" part.




Patch:
diff --git a/keycloak/keycloak_client.go b/keycloak/keycloak_client.go
index ee8638b..bc6f66a 100644
--- a/keycloak/keycloak_client.go
+++ b/keycloak/keycloak_client.go
@@ -40,11 +40,11 @@ type ClientCredentials struct {
 }
 
 const (
-	apiUrl   = "/auth/admin"
-	tokenUrl = "%s/auth/realms/%s/protocol/openid-connect/token"
+	apiUrl   = "/admin"
+	tokenUrl = "%s/realms/%s/protocol/openid-connect/token"
 )
 
-func NewKeycloakClient(baseUrl, clientId, clientSecret, realm, username, password string, initialLogin bool, clientTimeout int, caCert string, tlsInsecureSkipVerify bool, userAgent string) (*KeycloakClient, error) {
+func NewKeycloakClient(url, basePath, clientId, clientSecret, realm, username, password string, initialLogin bool, clientTimeout int, caCert string, tlsInsecureSkipVerify bool, userAgent string) (*KeycloakClient, error) {
 	cookieJar, err := cookiejar.New(&cookiejar.Options{
 		PublicSuffixList: publicsuffix.List,
 	})
@@ -87,7 +87,7 @@ func NewKeycloakClient(baseUrl, clientId, clientSecret, realm, username, passwor
 	}
 
 	keycloakClient := KeycloakClient{
-		baseUrl:           baseUrl,
+		baseUrl:           url + basePath,
 		clientCredentials: clientCredentials,
 		httpClient:        httpClient,
 		initialLogin:      initialLogin,
@@ -122,6 +122,9 @@ func (keycloakClient *KeycloakClient) login() error {
 	if err != nil {
 		return err
 	}
+	if accessTokenResponse.StatusCode != http.StatusOK {
+		return fmt.Errorf("error sending POST request to %s: %s", accessTokenUrl, accessTokenResponse.Status)
+	}
 
 	defer accessTokenResponse.Body.Close()
 
diff --git a/provider/provider.go b/provider/provider.go
index 145f3ba..e334c17 100644
--- a/provider/provider.go
+++ b/provider/provider.go
@@ -142,6 +142,11 @@ func KeycloakProvider() *schema.Provider {
 				Description: "Allows ignoring insecure certificates when set to true. Defaults to false. Disabling security check is dangerous and should be avoided.",
 				Default:     false,
 			},
+			"base_path": {
+				Optional: true,
+				Type:     schema.TypeString,
+				Default:  "/auth",
+			},
 		},
 	}
 
@@ -163,6 +168,7 @@ func KeycloakProvider() *schema.Provider {
 
 func configureKeycloakProvider(data *schema.ResourceData, userAgent string) (interface{}, error) {
 	url := data.Get("url").(string)
+	basePath := data.Get("base_path").(string)
 	clientId := data.Get("client_id").(string)
 	clientSecret := data.Get("client_secret").(string)
 	username := data.Get("username").(string)
@@ -173,5 +179,5 @@ func configureKeycloakProvider(data *schema.ResourceData, userAgent string) (int
 	tlsInsecureSkipVerify := data.Get("tls_insecure_skip_verify").(bool)
 	rootCaCertificate := data.Get("root_ca_certificate").(string)
 
-	return keycloak.NewKeycloakClient(url, clientId, clientSecret, realm, username, password, initialLogin, clientTimeout, rootCaCertificate, tlsInsecureSkipVerify, userAgent)
+	return keycloak.NewKeycloakClient(url, basePath, clientId, clientSecret, realm, username, password, initialLogin, clientTimeout, rootCaCertificate, tlsInsecureSkipVerify, userAgent)
 }


Test patch:
diff --git a/keycloak/keycloak_client_test.go b/keycloak/keycloak_client_test.go
index 57a0deb..5925402 100644
--- a/keycloak/keycloak_client_test.go
+++ b/keycloak/keycloak_client_test.go
@@ -51,7 +51,7 @@ func TestAccKeycloakApiClientRefresh(t *testing.T) {
 		t.Fatal("KEYCLOAK_CLIENT_TIMEOUT must be an integer")
 	}
 
-	keycloakClient, err := NewKeycloakClient(os.Getenv("KEYCLOAK_URL"), os.Getenv("KEYCLOAK_CLIENT_ID"), os.Getenv("KEYCLOAK_CLIENT_SECRET"), os.Getenv("KEYCLOAK_REALM"), os.Getenv("KEYCLOAK_USER"), os.Getenv("KEYCLOAK_PASSWORD"), true, clientTimeout, "", false, "")
+	keycloakClient, err := NewKeycloakClient(os.Getenv("KEYCLOAK_URL"), "/auth", os.Getenv("KEYCLOAK_CLIENT_ID"), os.Getenv("KEYCLOAK_CLIENT_SECRET"), os.Getenv("KEYCLOAK_REALM"), os.Getenv("KEYCLOAK_USER"), os.Getenv("KEYCLOAK_PASSWORD"), true, clientTimeout, "", false, "")
 	if err != nil {
 		t.Fatalf("%s", err)
 	}
diff --git a/provider/provider_test.go b/provider/provider_test.go
index f4a4244..f1d1916 100644
--- a/provider/provider_test.go
+++ b/provider/provider_test.go
@@ -26,7 +26,7 @@ func init() {
 		"keycloak": testAccProvider,
 	}
 
-	keycloakClient, _ = keycloak.NewKeycloakClient(os.Getenv("KEYCLOAK_URL"), os.Getenv("KEYCLOAK_CLIENT_ID"), os.Getenv("KEYCLOAK_CLIENT_SECRET"), os.Getenv("KEYCLOAK_REALM"), "", "", true, 5, "", false, httpclient.TerraformUserAgent(testAccProvider.TerraformVersion))
+	keycloakClient, _ = keycloak.NewKeycloakClient(os.Getenv("KEYCLOAK_URL"), "/auth", os.Getenv("KEYCLOAK_CLIENT_ID"), os.Getenv("KEYCLOAK_CLIENT_SECRET"), os.Getenv("KEYCLOAK_REALM"), "", "", true, 5, "", false, httpclient.TerraformUserAgent(testAccProvider.TerraformVersion))
 }
 
 func TestProvider(t *testing.T) {