Skip to content

Commit

Permalink
new data source: keycloak_authentication_flow (#486)
Browse files Browse the repository at this point in the history
  • Loading branch information
Zeldhyr authored Mar 12, 2021
1 parent e781451 commit 2df0fac
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 0 deletions.
30 changes: 30 additions & 0 deletions docs/data-sources/authentication_flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
page_title: "keycloak_authentication_flow Data Source"
---

# keycloak\_authentication\_flow Data Source

This data source can be used to fetch the ID of an authentication flow within Keycloak.

## Example Usage

```hcl
resource "keycloak_realm" "realm" {
realm = "my-realm"
enabled = true
}
data "keycloak_authentication_flow" "browser_auth_cookie" {
realm_id = keycloak_realm.realm.id
alias = "browser"
}
```

## Argument Reference

- `realm_id` - (Required) The realm the authentication flow exists in.
- `alias` - (Required) The alias of the flow.

## Attributes Reference

- `id` - (Computed) The unique ID of the authentication flow, which can be used as an argument to other resources supported by this provider.
45 changes: 45 additions & 0 deletions keycloak/authentication_flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keycloak

import (
"fmt"
"time"
)

type AuthenticationFlow struct {
Expand Down Expand Up @@ -53,6 +54,50 @@ func (keycloakClient *KeycloakClient) GetAuthenticationFlow(realmId, id string)
return &authenticationFlow, nil
}

func (keycloakClient *KeycloakClient) GetAuthenticationFlowFromAlias(realmId, alias string) (*AuthenticationFlow, error) {
var authenticationFlows []*AuthenticationFlow
var authenticationFlow *AuthenticationFlow = nil

err := keycloakClient.get(fmt.Sprintf("/realms/%s/authentication/flows", realmId), &authenticationFlows, nil)
if err != nil {
return nil, err
}

// Retry 3 more times if not found, sometimes it took split milliseconds the Authentication to populate
if len(authenticationFlows) == 0 {
for i := 0; i < 3; i++ {
err := keycloakClient.get(fmt.Sprintf("/realms/%s/authentication/flows", realmId), &authenticationFlows, nil)

if len(authenticationFlows) > 0 {
break
}

if err != nil {
return nil, err
}

time.Sleep(time.Millisecond * 50)
}

if len(authenticationFlows) == 0 {
return nil, fmt.Errorf("no authentication flow found for alias %s", alias)
}
}

for _, authFlow := range authenticationFlows {
if authFlow.Alias == alias {
authenticationFlow = authFlow
}
}

if authenticationFlow == nil {
return nil, fmt.Errorf("no authentication flow found for alias %s", alias)
}
authenticationFlow.RealmId = realmId

return authenticationFlow, nil
}

func (keycloakClient *KeycloakClient) UpdateAuthenticationFlow(authenticationFlow *AuthenticationFlow) error {
authenticationFlow.TopLevel = true
authenticationFlow.BuiltIn = false
Expand Down
38 changes: 38 additions & 0 deletions provider/data_source_keycloak_authentication_flow.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package provider

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

func dataSourceKeycloakAuthenticationFlow() *schema.Resource {
return &schema.Resource{
Read: dataSourceKeycloakAuthenticationFlowRead,
Schema: map[string]*schema.Schema{
"realm_id": {
Type: schema.TypeString,
Required: true,
},
"alias": {
Type: schema.TypeString,
Required: true,
},
},
}
}

func dataSourceKeycloakAuthenticationFlowRead(data *schema.ResourceData, meta interface{}) error {
keycloakClient := meta.(*keycloak.KeycloakClient)

realmID := data.Get("realm_id").(string)
alias := data.Get("alias").(string)

authenticationFlowInfo, err := keycloakClient.GetAuthenticationFlowFromAlias(realmID, alias)
if err != nil {
return err
}

mapFromAuthenticationFlowInfoToData(data, authenticationFlowInfo)

return nil
}
116 changes: 116 additions & 0 deletions provider/data_source_keycloak_authentication_flow_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package provider

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func TestAccKeycloakDataSourceAuthenticationFlow_basic(t *testing.T) {
t.Parallel()
alias := acctest.RandomWithPrefix("tf-acc")

resource.Test(t, resource.TestCase{
ProviderFactories: testAccProviderFactories,
PreCheck: func() { testAccPreCheck(t) },
CheckDestroy: testAccCheckKeycloakAuthenticationFlowDestroy(),
Steps: []resource.TestStep{
{
Config: testDataSourceKeycloakAuthenticationFlow_basic(alias),
Check: resource.ComposeTestCheckFunc(
testAccCheckKeycloakAuthenticationFlowExists("keycloak_authentication_flow.flow"),
resource.TestCheckResourceAttrPair("keycloak_authentication_flow.flow", "id", "data.keycloak_authentication_flow.flow", "id"),
testAccCheckDataKeycloakAuthenticationFlow("data.keycloak_authentication_flow.flow"),
),
},
},
})
}

func testAccCheckDataKeycloakAuthenticationFlow(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("resource not found: %s", resourceName)
}

id := rs.Primary.ID
realmID := rs.Primary.Attributes["realm_id"]

authenticationFlow, err := keycloakClient.GetAuthenticationFlow(realmID, id)
if err != nil {
return err
}

if authenticationFlow.Id != id {
return fmt.Errorf("expected authenticationFlow with ID %s but got %s", id, authenticationFlow.Id)
}

return nil
}
}

func TestAccKeycloakDataSourceAuthenticationExecution_wrongAlias(t *testing.T) {
t.Parallel()
alias := acctest.RandomWithPrefix("tf-acc")

resource.Test(t, resource.TestCase{
ProviderFactories: testAccProviderFactories,
PreCheck: func() { testAccPreCheck(t) },
CheckDestroy: testAccCheckKeycloakAuthenticationFlowDestroy(),
Steps: []resource.TestStep{
{
Config: testDataSourceKeycloakAuthenticationFlow_wrongAlias(alias),
ExpectError: regexp.MustCompile("no authentication flow found for alias .*"),
},
},
})
}

func testDataSourceKeycloakAuthenticationFlow_basic(alias string) string {
return fmt.Sprintf(`
data "keycloak_realm" "realm" {
realm = "%s"
}
resource "keycloak_authentication_flow" "flow" {
realm_id = data.keycloak_realm.realm.id
alias = "%s"
}
data "keycloak_authentication_flow" "flow" {
realm_id = data.keycloak_realm.realm.id
alias = keycloak_authentication_flow.flow.alias
depends_on = [
keycloak_authentication_flow.flow,
]
}
`, testAccRealm.Realm, alias)
}

func testDataSourceKeycloakAuthenticationFlow_wrongAlias(alias string) string {
return fmt.Sprintf(`
data "keycloak_realm" "realm" {
realm = "%s"
}
resource "keycloak_authentication_flow" "flow" {
realm_id = data.keycloak_realm.realm.id
alias = "%s"
}
data "keycloak_authentication_flow" "flow" {
realm_id = data.keycloak_realm.realm.id
alias = "foo"
depends_on = [
keycloak_authentication_flow.flow,
]
}
`, testAccRealm.Realm, alias)
}
1 change: 1 addition & 0 deletions provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func KeycloakProvider(client *keycloak.KeycloakClient) *schema.Provider {
"keycloak_saml_client_installation_provider": dataSourceKeycloakSamlClientInstallationProvider(),
"keycloak_saml_client": dataSourceKeycloakSamlClient(),
"keycloak_authentication_execution": dataSourceKeycloakAuthenticationExecution(),
"keycloak_authentication_flow": dataSourceKeycloakAuthenticationFlow(),
},
ResourcesMap: map[string]*schema.Resource{
"keycloak_realm": resourceKeycloakRealm(),
Expand Down
6 changes: 6 additions & 0 deletions provider/resource_keycloak_authentication_flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ func mapFromAuthenticationFlowToData(data *schema.ResourceData, authenticationFl
data.Set("description", authenticationFlow.Description)
}

func mapFromAuthenticationFlowInfoToData(data *schema.ResourceData, authenticationFlow *keycloak.AuthenticationFlow) {
data.SetId(authenticationFlow.Id)
data.Set("realm_id", authenticationFlow.RealmId)
data.Set("alias", authenticationFlow.Alias)
}

func resourceKeycloakAuthenticationFlowCreate(data *schema.ResourceData, meta interface{}) error {
keycloakClient := meta.(*keycloak.KeycloakClient)

Expand Down

0 comments on commit 2df0fac

Please # to comment.