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

Data source for user and authentication execution #360

44 changes: 44 additions & 0 deletions keycloak/authentication_execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keycloak

import (
"fmt"
"time"
)

// this is only used when creating an execution on a flow.
Expand Down Expand Up @@ -73,6 +74,49 @@ func (keycloakClient *KeycloakClient) ListAuthenticationExecutions(realmId, pare
return authenticationExecutions, err
}

func (keycloakClient *KeycloakClient) GetAuthenticationExecutionInfoFromProviderId(realmId, parentFlowAlias, providerId string) (*AuthenticationExecutionInfo, error) {
var authenticationExecutions []*AuthenticationExecutionInfo
var authenticationExecution AuthenticationExecutionInfo

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

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

if len(authenticationExecutions) > 0 {
break
}

if err != nil {
return nil, err
}

time.Sleep(time.Millisecond * 50)
}

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

for _, aExecution := range authenticationExecutions {
if aExecution != nil && aExecution.ProviderId == providerId {
authenticationExecution = *aExecution
authenticationExecution.RealmId = realmId
authenticationExecution.ParentFlowAlias = parentFlowAlias

return &authenticationExecution, nil
}
}

return nil, fmt.Errorf("no authentication execution under parent flow alias %s with provider id %s found", parentFlowAlias, providerId)
}

func (keycloakClient *KeycloakClient) NewAuthenticationExecution(execution *AuthenticationExecution) error {
_, location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/authentication/flows/%s/executions/execution", execution.RealmId, execution.ParentFlowAlias), &authenticationExecutionCreate{Provider: execution.Authenticator})
if err != nil {
Expand Down
43 changes: 43 additions & 0 deletions provider/data_source_keycloak_authentication_execution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package provider

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

func dataSourceKeycloakAuthenticationExecution() *schema.Resource {
return &schema.Resource{
Read: dataSourceKeycloakAuthenticationExecutionRead,
Schema: map[string]*schema.Schema{
"realm_id": {
Type: schema.TypeString,
Required: true,
},
"parent_flow_alias": {
Type: schema.TypeString,
Required: true,
},
"provider_id": {
Type: schema.TypeString,
Required: true,
},
},
}
}

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

realmID := data.Get("realm_id").(string)
parentFlowAlias := data.Get("parent_flow_alias").(string)
providerID := data.Get("provider_id").(string)

authenticationExecutionInfo, err := keycloakClient.GetAuthenticationExecutionInfoFromProviderId(realmID, parentFlowAlias, providerID)
if err != nil {
return err
}

mapFromAuthenticationExecutionInfoToData(data, authenticationExecutionInfo)

return nil
}
171 changes: 171 additions & 0 deletions provider/data_source_keycloak_authentication_execution_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
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"
"github.com/mrparkers/terraform-provider-keycloak/keycloak"
)

func TestAccKeycloakDataSourceAuthenticationExecution_basic(t *testing.T) {
realm := "terraform-" + acctest.RandString(10)
parentFlowAlias := acctest.RandString(20)

resource.Test(t, resource.TestCase{
ProviderFactories: testAccProviderFactories,
PreCheck: func() { testAccPreCheck(t) },
CheckDestroy: testAccCheckKeycloakAuthenticationExecutionConfigDestroy,
Steps: []resource.TestStep{
{
Config: testDataSourceKeycloakAuthenticationExecution_basic(realm, parentFlowAlias),
Check: resource.ComposeTestCheckFunc(
testAccCheckKeycloakAuthenticationExecutionExists("keycloak_authentication_execution.execution"),
resource.TestCheckResourceAttrPair("keycloak_authentication_execution.execution", "id", "data.keycloak_authentication_execution.execution", "id"),
resource.TestCheckResourceAttrPair("keycloak_authentication_execution.execution", "realm_id", "data.keycloak_authentication_execution.execution", "realm_id"),
resource.TestCheckResourceAttrPair("keycloak_authentication_execution.execution", "parent_flow_alias", "data.keycloak_authentication_execution.execution", "parent_flow_alias"),
resource.TestCheckResourceAttrPair("keycloak_authentication_execution.execution", "authenticator", "data.keycloak_authentication_execution.execution", "provider_id"),
testAccCheckDataKeycloakAuthenticationExecution("data.keycloak_authentication_execution.execution"),
),
},
},
})
}

func TestAccKeycloakDataSourceAuthenticationExecution_errorNoExecutions(t *testing.T) {
realm := "terraform-" + acctest.RandString(10)
parentFlowAlias := acctest.RandString(20)

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

func TestAccKeycloakDataSourceAuthenticationExecution_errorWrongProviderId(t *testing.T) {
realm := "terraform-" + acctest.RandString(10)
parentFlowAlias := acctest.RandString(20)

resource.Test(t, resource.TestCase{
ProviderFactories: testAccProviderFactories,
PreCheck: func() { testAccPreCheck(t) },
CheckDestroy: testAccCheckKeycloakAuthenticationExecutionConfigDestroy,
Steps: []resource.TestStep{
{
Config: testDataSourceKeycloakAuthenticationExecution_errorWrongProviderId(realm, parentFlowAlias, acctest.RandString(10)),
ExpectError: regexp.MustCompile("no authentication execution under parent flow alias .* with provider id .* found"),
},
},
})
}

func testAccCheckDataKeycloakAuthenticationExecution(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)
}

keycloakClient := testAccProvider.Meta().(*keycloak.KeycloakClient)

id := rs.Primary.ID
realmID := rs.Primary.Attributes["realm_id"]
parentFlowAlias := rs.Primary.Attributes["parent_flow_alias"]
providerID := rs.Primary.Attributes["provider_id"]

authenticationExecutionInfo, err := keycloakClient.GetAuthenticationExecutionInfoFromProviderId(realmID, parentFlowAlias, providerID)
if err != nil {
return err
}

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

return nil
}
}

func testDataSourceKeycloakAuthenticationExecution_basic(realm, parentFlowAlias string) string {
return fmt.Sprintf(`
resource "keycloak_realm" "realm" {
realm = "%s"
enabled = true
}

resource "keycloak_authentication_flow" "flow" {
realm_id = keycloak_realm.realm.id
alias = "%s"
}

resource "keycloak_authentication_execution" "execution" {
realm_id = keycloak_realm.realm.id
parent_flow_alias = keycloak_authentication_flow.flow.alias
authenticator = "identity-provider-redirector"
requirement = "REQUIRED"
}

data "keycloak_authentication_execution" "execution" {
realm_id = keycloak_realm.realm.id
parent_flow_alias = keycloak_authentication_flow.flow.alias
provider_id = "identity-provider-redirector"
}
`, realm, parentFlowAlias)
}

func testDataSourceKeycloakAuthenticationExecution_errorNoExecutions(realm, parentFlowAlias string) string {
return fmt.Sprintf(`
resource "keycloak_realm" "realm" {
realm = "%s"
enabled = true
}

resource "keycloak_authentication_flow" "flow" {
realm_id = keycloak_realm.realm.id
alias = "%s"
}

data "keycloak_authentication_execution" "execution" {
realm_id = keycloak_realm.realm.id
parent_flow_alias = keycloak_authentication_flow.flow.alias
provider_id = "foo"
}
`, realm, parentFlowAlias)
}

func testDataSourceKeycloakAuthenticationExecution_errorWrongProviderId(realm, parentFlowAlias, providerId string) string {
return fmt.Sprintf(`
resource "keycloak_realm" "realm" {
realm = "%s"
enabled = true
}

resource "keycloak_authentication_flow" "flow" {
realm_id = keycloak_realm.realm.id
alias = "%s"
}

resource "keycloak_authentication_execution" "execution" {
realm_id = keycloak_realm.realm.id
parent_flow_alias = keycloak_authentication_flow.flow.alias
authenticator = "identity-provider-redirector"
requirement = "REQUIRED"
}

data "keycloak_authentication_execution" "execution" {
realm_id = keycloak_realm.realm.id
parent_flow_alias = keycloak_authentication_flow.flow.alias
provider_id = "%s"
}
`, realm, parentFlowAlias, providerId)
}
67 changes: 67 additions & 0 deletions provider/data_source_keycloak_user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package provider

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

func dataSourceKeycloakUser() *schema.Resource {
return &schema.Resource{
Read: dataSourceKeycloakUserRead,
Schema: map[string]*schema.Schema{
"realm_id": {
Type: schema.TypeString,
Required: true,
},
"username": {
Type: schema.TypeString,
Required: true,
},
"email": {
Type: schema.TypeString,
Computed: true,
},
"email_verified": {
Type: schema.TypeBool,
Computed: true,
},
"first_name": {
Type: schema.TypeString,
Computed: true,
},
"last_name": {
Type: schema.TypeString,
Computed: true,
},
"attributes": {
Type: schema.TypeMap,
Computed: true,
},
"federated_identity": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
},
"enabled": {
Type: schema.TypeBool,
Computed: true,
},
},
}
}

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

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

user, err := keycloakClient.GetUserByUsername(realmID, username)
if err != nil {
return err
}

mapFromUserToData(data, user)
rjmasikome marked this conversation as resolved.
Show resolved Hide resolved

return nil
}
Loading