in providers/keycloak/generator.go [32:347]
func (g *RealmGenerator) InitResources() error {
var realms []*keycloak.Realm
var realmsGroups []*keycloak.Group
ctx := context.TODO()
// Connect to keycloak instance
userAgent := "GoogleCloudPlatform Terraformer/0.8.22 (+https://github.com/GoogleCloudPlatform/terraformer) Terraform Plugin SDK/2.10.1"
kck, err := keycloak.NewKeycloakClient(ctx, g.GetArgs()["url"].(string), g.GetArgs()["base_path"].(string), g.GetArgs()["client_id"].(string), g.GetArgs()["client_secret"].(string), g.GetArgs()["realm"].(string), "", "", true, g.GetArgs()["client_timeout"].(int), g.GetArgs()["root_ca_certificate"].(string), g.GetArgs()["tls_insecure_skip_verify"].(bool), userAgent, g.GetArgs()["red_hat_sso"].(bool), make(map[string]string))
if err != nil {
return errors.New("keycloak: could not connect to Keycloak")
}
// Get realm resources
target := g.GetArgs()["target"].(string)
if target == "" {
realms, err = kck.GetRealms(ctx)
if err != nil {
return errors.New("keycloak: could not get realms attributes in Keycloak")
}
} else {
realm, err := kck.GetRealm(ctx, target)
if err != nil {
return errors.New("keycloak: could not get " + target + " realm attributes in Keycloak")
}
realms = append(realms, realm)
}
g.Resources = append(g.Resources, g.createRealmResources(realms)...)
// For each realm, get resources
for _, realm := range realms {
// Get required actions resources
requiredActions, err := kck.GetRequiredActions(ctx, realm.Realm)
if err != nil {
return fmt.Errorf("keycloak: could not get required actions of realm %s in Keycloak. err: %w", realm.Realm, err)
}
g.Resources = append(g.Resources, g.createRequiredActionResources(requiredActions)...)
// Get top-level authentication flows resources
authenticationFlows, err := kck.ListAuthenticationFlows(ctx, realm.Realm)
if err != nil {
return fmt.Errorf("keycloak: could not get authentication flows of realm %s in Keycloak. err: %w", realm.Realm, err)
}
g.Resources = append(g.Resources, g.createAuthenticationFlowResources(authenticationFlows)...)
// For each authentication flow, get subFlow, execution and execution config resources
for _, topLevelAuthenticationFlow := range authenticationFlows {
authenticationSubFlowOrExecutions, err := kck.ListAuthenticationExecutions(ctx, realm.Realm, topLevelAuthenticationFlow.Alias)
if err != nil {
return fmt.Errorf("keycloak: could not get authentication executions of authentication flow %s of realm %s in Keycloak. err: %w",
topLevelAuthenticationFlow.Alias, realm.Realm, err)
}
var stack []*keycloak.AuthenticationExecutionInfo
parentFlowAlias := topLevelAuthenticationFlow.Alias
for _, authenticationSubFlowOrExecution := range authenticationSubFlowOrExecutions {
// Find the parent flow alias
if len(stack) > 0 {
previous := stack[len(stack)-1]
if authenticationSubFlowOrExecution.Level < previous.Level {
// Find the last sub flow/execution for the current level
stack = stack[:authenticationSubFlowOrExecution.Level+1]
previous = stack[len(stack)-1]
}
if authenticationSubFlowOrExecution.Level == previous.Level {
// Same level sub flow/execution, it means that the sub flow/execution has same parent flow of the last sub flow/execution
parentFlowAlias = previous.ParentFlowAlias
} else if authenticationSubFlowOrExecution.Level > previous.Level {
// Deep level sub flow/execution, it means that the parent flow is the last sub flow/execution
if previous.AuthenticationFlow {
parentFlowAlias = previous.Alias
} else {
return errors.New("keycloak: invalid parent sub flow, it should be a sub flow but it's an execution")
}
}
}
var resource terraformutils.Resource
switch authenticationSubFlowOrExecution.AuthenticationFlow {
case true:
authenticationSubFlow, err := kck.GetAuthenticationSubFlow(ctx, realm.Realm, parentFlowAlias, authenticationSubFlowOrExecution.FlowId)
if err != nil {
return fmt.Errorf("keycloak: could not get authentication subflow %s of realm %s in Keycloak. err: %w",
authenticationSubFlowOrExecution.FlowId, realm.Realm, err)
}
// Need to store the alias and parent flow alias
authenticationSubFlowOrExecution.Alias = authenticationSubFlow.Alias
authenticationSubFlowOrExecution.ParentFlowAlias = parentFlowAlias
resource = g.createAuthenticationSubFlowResource(authenticationSubFlow)
g.Resources = append(g.Resources, resource)
case false:
authenticationExecution, err := kck.GetAuthenticationExecution(ctx, realm.Realm, parentFlowAlias, authenticationSubFlowOrExecution.Id)
if err != nil {
return fmt.Errorf("keycloak: could not get authentication execution %s of realm %s in Keycloak. err: %w",
authenticationSubFlowOrExecution.Id, realm.Realm, err)
}
// Need to store the parent flow alias
authenticationSubFlowOrExecution.ParentFlowAlias = parentFlowAlias
resource = g.createAuthenticationExecutionResource(authenticationExecution)
g.Resources = append(g.Resources, resource)
if authenticationSubFlowOrExecution.AuthenticationConfig != "" {
authenticationExecutionConfig := &keycloak.AuthenticationExecutionConfig{
RealmId: realm.Realm,
Id: authenticationSubFlowOrExecution.AuthenticationConfig,
ExecutionId: authenticationSubFlowOrExecution.Id,
}
err := kck.GetAuthenticationExecutionConfig(ctx, authenticationExecutionConfig)
if err != nil {
return fmt.Errorf("keycloak: could not get authentication execution config %s of realm %s in Keycloak. err: %w",
authenticationExecutionConfig.Id, realm.Realm, err)
}
g.Resources = append(g.Resources, g.createAuthenticationExecutionConfigResource(authenticationExecutionConfig))
}
}
if len(stack) > 0 && authenticationSubFlowOrExecution.Index > 0 {
previous := stack[len(stack)-1]
var resourceType string
var resourceName string
if previous.AuthenticationFlow {
resourceType = "keycloak_authentication_subflow"
resourceName = "authentication_subflow_" +
normalizeResourceName(realm.Realm) + "_" + normalizeResourceName(previous.FlowId)
} else {
resourceType = "keycloak_authentication_execution"
resourceName = "authentication_execution_" +
normalizeResourceName(realm.Realm) + "_" + normalizeResourceName(previous.Id)
}
resource.AdditionalFields["depends_on"] = []string{resourceType + "." + terraformutils.TfSanitize(resourceName)}
}
// Stack the current sub flow/execution
if len(stack) > 0 && stack[len(stack)-1].Level == authenticationSubFlowOrExecution.Level {
// Replace it if it's same level
stack[len(stack)-1] = authenticationSubFlowOrExecution
} else {
stack = append(stack, authenticationSubFlowOrExecution)
}
}
}
// Get custom federations resources
// TODO: support kerberos user federation
customUserFederations, err := kck.GetCustomUserFederations(ctx, realm.Realm, realm.Id)
if err != nil {
return errors.New("keycloak: could not get custom user federations of realm " + realm.Id + " in Keycloak")
}
g.Resources = append(g.Resources, g.createCustomUserFederationResources(customUserFederations)...)
// For each custom federation, get mappers resources
for _, customUserFederation := range *customUserFederations {
if customUserFederation.ProviderId == "ldap" {
mappers, err := kck.GetLdapUserFederationMappers(ctx, realm.Realm, customUserFederation.Id)
if err != nil {
return errors.New("keycloak: could not get mappers of ldap user federation " + customUserFederation.Name + " of realm " + realm.Realm + " in Keycloak")
}
g.Resources = append(g.Resources, g.createLdapMapperResources(realm.Realm, customUserFederation.Name, mappers)...)
}
}
// Get groups tree and default groups resources
realmGroups, err := kck.GetGroups(ctx, realm.Realm)
if err != nil {
return errors.New("keycloak: could not get groups of realm " + realm.Realm + " in Keycloak")
}
realmsGroups = append(realmsGroups, realmGroups...)
g.Resources = append(g.Resources, g.createDefaultGroupResource(realm.Realm))
// Get users resources
realmUsers, err := kck.GetUsers(ctx, realm.Realm)
if err != nil {
return errors.New("keycloak: could not get users of realm " + realm.Realm + " in Keycloak")
}
g.Resources = append(g.Resources, g.createUserResources(realmUsers)...)
// Get realm open id client scopes resources
realmScopes, err := kck.ListOpenidClientScopesWithFilter(ctx, realm.Realm, func(scope *keycloak.OpenidClientScope) bool { return true })
if err != nil {
return errors.New("keycloak: could not get realm scopes of realm " + realm.Realm + " in Keycloak")
}
g.Resources = append(g.Resources, g.createScopeResources(realm.Realm, realmScopes)...)
// Get open id clients
realmClients, err := kck.GetOpenidClients(ctx, realm.Realm, true)
if err != nil {
return errors.New("keycloak: could not get open id clients of realm " + realm.Realm + " in Keycloak")
}
g.Resources = append(g.Resources, g.createOpenIDClientResources(realmClients)...)
// For each open id client, get resources
mapServiceAccountIds := map[string]map[string]string{}
mapContainerIDs := map[string]string{}
mapClientIDs := map[string]string{}
for _, client := range realmClients {
mapClientIDs[client.Id] = client.ClientId
mapContainerIDs[client.Id] = "_" + client.ClientId
// Get open id client protocol mappers resources
clientMappers, err := kck.GetGenericProtocolMappers(ctx, realm.Realm, client.Id)
if err != nil {
return errors.New("keycloak: could not get protocol mappers of open id client " + client.ClientId + " of realm " + realm.Realm + " in Keycloak")
}
g.Resources = append(g.Resources, g.createOpenIDProtocolMapperResources(client.ClientId, clientMappers)...)
// Get open id client default scopes resources
clientScopes, err := kck.GetOpenidDefaultClientScopes(ctx, realm.Realm, client.Id)
if err != nil {
return errors.New("keycloak: could not get default client scopes of open id client " + client.ClientId + " of realm " + realm.Realm + " in Keycloak")
}
if len(*clientScopes) > 0 {
g.Resources = append(g.Resources, g.createOpenidClientScopesResources(realm.Realm, client.Id, client.ClientId, "default", clientScopes))
}
// Get open id client optional scopes resources
clientScopes, err = kck.GetOpenidOptionalClientScopes(ctx, realm.Realm, client.Id)
if err != nil {
return errors.New("keycloak: could not get optional client scopes of open id client " + client.ClientId + " of realm " + realm.Realm + " in Keycloak")
}
if len(*clientScopes) > 0 {
g.Resources = append(g.Resources, g.createOpenidClientScopesResources(realm.Realm, client.Id, client.ClientId, "optional", clientScopes))
}
// Prepare a slice to be able to link roles associated to service account roles to be associated to the open id client, only if service accounts are enabled
if !client.ServiceAccountsEnabled {
continue
}
serviceAccountUser, err := kck.GetOpenidClientServiceAccountUserId(ctx, realm.Realm, client.Id)
if err != nil {
return errors.New("keycloak: could not get service account user associated to open id client " + client.ClientId + " of realm " + realm.Realm + " in Keycloak")
}
mapServiceAccountIds[serviceAccountUser.Id] = map[string]string{}
mapServiceAccountIds[serviceAccountUser.Id]["Id"] = client.Id
mapServiceAccountIds[serviceAccountUser.Id]["ClientId"] = client.ClientId
}
// Get open id client roles
clientRoles, err := kck.GetClientRoles(ctx, realm.Realm, realmClients)
if err != nil {
return errors.New("keycloak: could not get open id clients roles of realm " + realm.Realm + " in Keycloak")
}
// Get roles
realmRoles, err := kck.GetRealmRoles(ctx, realm.Realm)
if err != nil {
return errors.New("keycloak: could not get realm roles of realm " + realm.Realm + " in Keycloak")
}
// Set ContainerId of the roles, for realm = "", for open id clients = "_" + client.ClientId
// and get roles resources
mapContainerIDs[realm.Realm] = ""
roles := append(clientRoles, realmRoles...)
for _, role := range roles {
role.ContainerId = mapContainerIDs[role.ContainerId]
}
g.Resources = append(g.Resources, g.createRoleResources(roles)...)
// Get service account roles resources
usersInRole, err := kck.GetClientRoleUsers(ctx, realm.Realm, clientRoles)
if err != nil {
return errors.New("keycloak: could not get users roles of realm " + realm.Realm + " in Keycloak")
}
g.Resources = append(g.Resources, g.createServiceAccountClientRolesResources(realm.Realm, clientRoles, *usersInRole, mapServiceAccountIds, mapClientIDs)...)
}
// Parse the groups trees, and get all the groups
// Get groups resources
groups := g.flattenGroups(realmsGroups, "")
g.Resources = append(g.Resources, g.createGroupResources(groups)...)
// For each group, get group memberships and roles resources
for _, group := range groups {
// Get group members resources
members, err := kck.GetGroupMembers(ctx, group.RealmId, group.Id)
if err != nil {
return errors.New("keycloak: could not get group members of group " + group.Name + " in Keycloak")
}
if len(members) > 0 {
groupMembers := make([]string, len(members))
for k, member := range members {
groupMembers[k] = member.Username
}
g.Resources = append(g.Resources, g.createGroupMembershipsResource(group.RealmId, group.Id, group.Name, groupMembers))
}
// Get group roles resources
// For realm roles and open id clients roles
groupDetails, err := kck.GetGroup(ctx, group.RealmId, group.Id)
if err != nil {
return errors.New("keycloak: could not get details about group " + group.Name + " in Keycloak")
}
groupRoles := []string{}
if len(groupDetails.RealmRoles) > 0 {
groupRoles = append(groupRoles, groupDetails.RealmRoles...)
}
if len(groupDetails.ClientRoles) > 0 {
for _, clientRoles := range groupDetails.ClientRoles {
groupRoles = append(groupRoles, clientRoles...)
}
}
if len(groupRoles) > 0 {
g.Resources = append(g.Resources, g.createGroupRolesResource(group.RealmId, group.Id, group.Name, groupRoles))
}
}
return nil
}