Repository: Nerzal/gocloak
Pull request #264 (2021-03-22 23:14:11)

added endpoint to support exporting public broker config info
This PR is adding support for Keycloak's API that allows the user to "Export public broker configuration for identity provider," which is documented here: https://www.keycloak.org/docs-api/5.0/rest-api/index.html#_identity_providers_resource

Before I began work on this feature, the many of the tests were not passing. This is what the test output looked like before I began development: 
[test_master_before_pr.txt](https://github.com/Nerzal/gocloak/files/6185792/test_master_before_pr.txt)

The only test that needed to be changed was TestGocloak_CreateProvider. This test was passing before the new client method was added. The test was updated to cover the new client method, and passes.  Additionally, I removed the Parallel flag, as the TestGocloak_CreateProvider is written with the expectation that each subtest is run sequentially. 
 


codecov[bot]: # [Codecov](https://codecov.io/gh/Nerzal/gocloak/pull/264?src=pr&el=h1) Report
> Merging [#264](https://codecov.io/gh/Nerzal/gocloak/pull/264?src=pr&el=desc) (133417e) into [master](https://codecov.io/gh/Nerzal/gocloak/commit/0bd22a70abd689ac2c36e3815dca45c880fc9ec9?el=desc) (0bd22a7) will **increase** coverage by `0.00%`.
> The diff coverage is `80.00%`.

[![Impacted file tree graph](https://codecov.io/gh/Nerzal/gocloak/pull/264/graphs/tree.svg?width=650&height=150&src=pr&token=gB52LSVNIV)](https://codecov.io/gh/Nerzal/gocloak/pull/264?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##           master     #264   +/-   ##
=======================================
  Coverage   79.25%   79.25%           
=======================================
  Files           4        4           
  Lines        1663     1673   +10     
=======================================
+ Hits         1318     1326    +8     
- Misses        217      218    +1     
- Partials      128      129    +1     
```


| [Impacted Files](https://codecov.io/gh/Nerzal/gocloak/pull/264?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [client.go](https://codecov.io/gh/Nerzal/gocloak/pull/264/diff?src=pr&el=tree#diff-Y2xpZW50Lmdv) | `77.60% <80.00%> (+0.01%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Nerzal/gocloak/pull/264?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Nerzal/gocloak/pull/264?src=pr&el=footer). Last update [0bd22a7...133417e](https://codecov.io/gh/Nerzal/gocloak/pull/264?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/client.go b/client.go
index d75c419..13e1dcd 100644
--- a/client.go
+++ b/client.go
@@ -62,6 +62,12 @@ func (client *gocloak) getRequestWithBearerAuth(ctx context.Context, token strin
 		SetHeader("Content-Type", "application/json")
 }
 
+func (client *gocloak) getRequestWithBearerAuthXMLHeader(ctx context.Context, token string) *resty.Request {
+	return client.getRequest(ctx).
+		SetAuthToken(token).
+		SetHeader("Content-Type", "application/xml;charset=UTF-8")
+}
+
 func (client *gocloak) getRequestWithBasicAuth(ctx context.Context, clientID, clientSecret string) *resty.Request {
 	req := client.getRequest(ctx).
 		SetHeader("Content-Type", "application/x-www-form-urlencoded")
@@ -2305,6 +2311,21 @@ func (client *gocloak) DeleteIdentityProvider(ctx context.Context, token, realm,
 	return checkForError(resp, err, errMessage)
 }
 
+// ExportIDPPublicBrokerConfig exports the broker config for a given alias
+func (client *gocloak) ExportIDPPublicBrokerConfig(ctx context.Context, token, realm, alias string) (*string, error) {
+	const errMessage = "could not get public identity provider configuration"
+
+	resp, err := client.getRequestWithBearerAuthXMLHeader(ctx, token).
+		Get(client.getAdminRealmURL(realm, "identity-provider", "instances", alias, "export"))
+
+	if err := checkForError(resp, err, errMessage); err != nil {
+		return nil, err
+	}
+
+	result := resp.String()
+	return &result, nil
+}
+
 // ------------------
 // Protection API
 // ------------------
diff --git a/gocloak.go b/gocloak.go
index f06ddf5..b2f59e5 100644
--- a/gocloak.go
+++ b/gocloak.go
@@ -316,6 +316,8 @@ type GoCloak interface {
 	UpdateIdentityProvider(ctx context.Context, token, realm, alias string, providerRep IdentityProviderRepresentation) error
 	// DeleteIdentityProvider deletes the identity provider in a realm
 	DeleteIdentityProvider(ctx context.Context, token, realm, alias string) error
+	// ExportIDPPublicBrokerConfig exports the broker config for a given alias
+	ExportIDPPublicBrokerConfig(ctx context.Context, token, realm, alias string) (*string, error)
 
 	// *** Protection API ***
 	// GetResource returns a client's resource with the given id, using access token from client


Test patch:
diff --git a/client_test.go b/client_test.go
index 2e875cf..291325c 100644
--- a/client_test.go
+++ b/client_test.go
@@ -3957,7 +3957,6 @@ func TestGocloak_CreateDeleteClientScopeWithMappers(t *testing.T) {
 // -----------------
 
 func TestGocloak_CreateProvider(t *testing.T) {
-	t.Parallel()
 	cfg := GetConfig(t)
 	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
@@ -4115,6 +4114,59 @@ func TestGocloak_CreateProvider(t *testing.T) {
 		)
 		require.NoError(t, err)
 	})
+
+	t.Run("create SAML provider", func(t *testing.T) {
+		repr := gocloak.IdentityProviderRepresentation{
+			Alias:                     gocloak.StringP("saml"),
+			DisplayName:               gocloak.StringP("Generic SAML"),
+			Enabled:                   gocloak.BoolP(true),
+			ProviderID:                gocloak.StringP("saml"),
+			TrustEmail:                gocloak.BoolP(true),
+			FirstBrokerLoginFlowAlias: gocloak.StringP("first broker login"),
+			Config: &map[string]string{
+				"singleSignOnServiceUrl": "https://samlIDPexample.com",
+			},
+		}
+		provider, err := client.CreateIdentityProvider(
+			context.Background(),
+			token.AccessToken,
+			cfg.GoCloak.Realm,
+			repr,
+		)
+		require.NoError(t, err)
+		require.Equal(t, "saml", provider)
+	})
+
+	t.Run("Get saml provider", func(t *testing.T) {
+		provider, err := client.GetIdentityProvider(
+			context.Background(),
+			token.AccessToken,
+			cfg.GoCloak.Realm,
+			"saml",
+		)
+		require.NoError(t, err)
+		require.Equal(t, "saml", *(provider.Alias))
+	})
+
+	t.Run("Get saml provider public broker config", func(t *testing.T) {
+		config, err := client.ExportIDPPublicBrokerConfig(
+			context.Background(),
+			token.AccessToken,
+			cfg.GoCloak.Realm,
+			"saml",
+		)
+		require.NoError(t, err)
+		require.NotEmpty(t, *(config))
+	})
+	t.Run("Delete saml provider", func(t *testing.T) {
+		err := client.DeleteIdentityProvider(
+			context.Background(),
+			token.AccessToken,
+			cfg.GoCloak.Realm,
+			"saml",
+		)
+		require.NoError(t, err)
+	})
 }
 
 // -----------------