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

Feat: Add codefresh_idp and codefresh_account_idp resource and codefresh_account_idp datasource #138

Merged
merged 28 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
73c723f
creation works, but state inconsistent
ilia-medvedev-codefresh Feb 4, 2024
f1c0ac4
creation fully works
ilia-medvedev-codefresh Feb 4, 2024
92bce15
crud works with list not picking drift caveat
ilia-medvedev-codefresh Feb 5, 2024
2a0816b
crud works with list not picking drift caveat
ilia-medvedev-codefresh Feb 5, 2024
41a104d
recreate if deleted
ilia-medvedev-codefresh Feb 6, 2024
3982229
replace works and drift is detected
ilia-medvedev-codefresh Feb 6, 2024
f2bfd3c
fix idp update unmarshall error
ilia-medvedev-codefresh Feb 6, 2024
4ba6c46
cleanup
ilia-medvedev-codefresh Feb 6, 2024
7cd7e32
add account scoped idp
ilia-medvedev-codefresh Feb 7, 2024
9d30f94
add account scoped idp
ilia-medvedev-codefresh Feb 7, 2024
0e9c042
add google and auth0
ilia-medvedev-codefresh Feb 8, 2024
3cd8e5c
add azure
ilia-medvedev-codefresh Feb 10, 2024
af8289f
Add onelogin and keycloak
ilia-medvedev-codefresh Feb 11, 2024
50741e9
add saml
ilia-medvedev-codefresh Feb 11, 2024
70420d7
finalize saml
ilia-medvedev-codefresh Feb 11, 2024
33f1eaf
add ldap and format
ilia-medvedev-codefresh Feb 12, 2024
4c63ac8
Split resources
ilia-medvedev-codefresh Feb 15, 2024
c49ef82
fmt and adjust test
ilia-medvedev-codefresh Feb 15, 2024
9d6a6c2
sort our provideraddr for debugging
ilia-medvedev-codefresh Feb 15, 2024
4a02dde
add tests for multiple identity providers
ilia-medvedev-codefresh Feb 19, 2024
60f32dd
update docs
ilia-medvedev-codefresh Feb 19, 2024
0302193
change description
ilia-medvedev-codefresh Feb 20, 2024
a5d8706
add datasource for account idp and update docs
ilia-medvedev-codefresh Feb 21, 2024
1321598
merge master
ilia-medvedev-codefresh Feb 26, 2024
fa552a1
feat: reusable IDP schema and types (#139)
korenyoni Feb 28, 2024
beb33c5
fix supported idp types array
ilia-medvedev-codefresh Feb 28, 2024
b0a6b99
Merge branch 'master' into feat/idp-resource
ilia-medvedev-codefresh Feb 28, 2024
f308771
Merge branch 'master' into feat/idp-resource
ilia-medvedev-codefresh Feb 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 207 additions & 26 deletions codefresh/cfclient/idp.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,200 @@ package cfclient
import (
"errors"
"fmt"
"log"
"net/url"
)

type IDP struct {
Access_token string `json:"access_token,omitempty"`
Accounts []string `json:"accounts,omitempty"`
ApiHost string `json:"apiHost,omitempty"`
ApiPathPrefix string `json:"apiPathPrefix,omitempty"`
ApiURL string `json:"apiURL,omitempty"`
AppId string `json:"appId,omitempty"`
AuthURL string `json:"authURL,omitempty"`
ClientHost string `json:"clientHost,omitempty"`
ClientId string `json:"clientId,omitempty"`
ClientName string `json:"clientName,omitempty"`
ClientSecret string `json:"clientSecret,omitempty"`
ClientType string `json:"clientType,omitempty"`
CookieIv string `json:"cookieIv,omitempty"`
CookieKey string `json:"cookieKey,omitempty"`
DisplayName string `json:"displayName,omitempty"`
ID string `json:"_id,omitempty"`
IDPLoginUrl string `json:"IDPLoginUrl,omitempty"`
LoginUrl string `json:"loginUrl,omitempty"`
RedirectUiUrl string `json:"redirectUiUrl,omitempty"`
RedirectUrl string `json:"redirectUrl,omitempty"`
RefreshTokenURL string `json:"refreshTokenURL,omitempty"`
Scopes []string `json:"scopes,omitempty"`
Tenant string `json:"tenant,omitempty"`
TokenSecret string `json:"tokenSecret,omitempty"`
TokenURL string `json:"tokenURL,omitempty"`
UserProfileURL string `json:"userProfileURL,omitempty"`
ID string `json:"_id,omitempty"`
Access_token string `json:"access_token,omitempty"`
Accounts []string `json:"accounts,omitempty"`
ClientName string `json:"clientName,omitempty"` // IDP name
ClientType string `json:"clientType,omitempty"` // IDP type
DisplayName string `json:"displayName,omitempty"`
LoginUrl string `json:"loginUrl,omitempty"` // Login url in Codefresh
RedirectUiUrl string `json:"redirectUiUrl,omitempty"` // Redicrect url Codefresh UI
RedirectUrl string `json:"redirectUrl,omitempty"`
ClientId string `json:"clientId,omitempty"` // All providers (base)
ClientSecret string `json:"clientSecret,omitempty"` // All providers (base)
ApiHost string `json:"apiHost,omitempty"` // GitHub
ApiPathPrefix string `json:"apiPathPrefix,omitempty"` // Github
// Bitbucket, Gitlab
ApiURL string `json:"apiURL,omitempty"`
// Azure, Okta, onelogin,saml
AppId string `json:"appId,omitempty"`
// Github, Gitlab
AuthURL string `json:"authURL,omitempty"`
// saml, okta, onelogin, auth0, azure, google, google-cloud-sr
ClientHost string `json:"clientHost,omitempty"`
// Azure
CookieIv string `json:"cookieIv,omitempty"`
// Azure
CookieKey string `json:"cookieKey,omitempty"`
// Azure
IDPLoginUrl string `json:"IDPLoginUrl,omitempty"`
// Bitbucket
RefreshTokenURL string `json:"refreshTokenURL,omitempty"`
// Multiple - computed
Scopes []string `json:"scopes,omitempty"`
// Azure
Tenant string `json:"tenant,omitempty"`
TokenSecret string `json:"tokenSecret,omitempty"`
// Okta, Bitbucket, GitHub, Keycloak
TokenURL string `json:"tokenURL,omitempty"`
// Github, Gitlab
UserProfileURL string `json:"userProfileURL,omitempty"`
// Okta
SyncMirrorAccounts []string `json:"syncMirrorAccounts,omitempty"`
// Google, Ldap
AllowedGroupsForSync string `json:"allowedGroupsForSync,omitempty"`
// Google
Subject string `json:"subject,omitempty"`
// Google
KeyFile string `json:"keyfile,omitempty"`
// Google
SyncField string `json:"syncField,omitempty"`
// Azure
AutoGroupSync bool `json:"autoGroupSync,omitempty"`
// Google,Okta,saml
ActivateUserAfterSync bool `json:"activateUserAfterSync,omitempty"`
// Azure
SyncInterval string `json:"syncInterval,omitempty"`
// Onelogin
ApiClientId string `json:"apiClientId,omitempty"`
// Onelogin
ApiClientSecret string `json:"apiClientSecret,omitempty"`
// Keycloak
Host string `json:"host,omitempty"`
// keycloak
Realm string `json:"realm,omitempty"`
// SAML
EntryPoint string `json:"entryPoint,omitempty"`
// SAML
ApplicationCert string `json:"cert,omitempty"`
// SAML
SamlProvider string `json:"provider,omitempty"`
// ldap
Password string `json:"password,omitempty"`
Url string `json:"url,omitempty"`
DistinguishedName string `json:"distinguishedName,omitempty"`
SearchBase string `json:"searchBase,omitempty"`
SearchFilter string `json:"searchFilter,omitempty"`
SearchBaseForSync string `json:"searchBaseForSync,omitempty"`
Certificate string `json:"certificate,omitempty"`
}

// Return the appropriate API endpoint for platform and account scoped IDPs
func getAPIEndpoint(isGlobal bool) string {
// If IDP is platform scoped
if isGlobal {
return "/admin/idp"
} else {
return "/idp/account"
}
}

// Currently on create the API sometimes (like when creating saml idps) returns a different structure for accounts than on read making the client crash on decode
// For now we are disabling response decode and in the resource will instead call the read function again
func (client *Client) CreateIDP(idp *IDP, isGlobal bool) (id string, err error) {

body, err := EncodeToJSON(idp)

if err != nil {
return "", err
}
opts := RequestOptions{
Path: getAPIEndpoint(isGlobal),
Method: "POST",
Body: body,
}

resp, err := client.RequestAPI(&opts)

if err != nil {
log.Printf("[DEBUG] Call to API for IDP creation failed with Error = %v for Body %v", err, body)
return "", err
}

var respIDP map[string]interface{}
err = DecodeResponseInto(resp, &respIDP)

if err != nil {
return "", nil
}

return respIDP["id"].(string), nil
}

// Currently on update the API returns a different structure for accounts than on read making the client crash on decode
// For now we are disabling response decode and in the resource will instead call the read function again
func (client *Client) UpdateIDP(idp *IDP, isGlobal bool) error {

body, err := EncodeToJSON(idp)

if err != nil {
return err
}
opts := RequestOptions{
Path: getAPIEndpoint(isGlobal),
Method: "PUT",
Body: body,
}

_, err = client.RequestAPI(&opts)

if err != nil {
log.Printf("[DEBUG] Call to API for IDP update failed with Error = %v for Body %v", err, body)
return err
}

// var respIDP IDP
// err = DecodeResponseInto(resp, &respIDP)
// if err != nil {
// return nil, err
// }

return nil
}

func (client *Client) DeleteIDP(id string) error {
baseUrl := getAPIEndpoint(true)
fullPath := fmt.Sprintf("%s/%s", baseUrl, url.PathEscape(id))
opts := RequestOptions{
Path: fullPath,
Method: "DELETE",
}

_, err := client.RequestAPI(&opts)

if err != nil {
return err
}

return nil
}

func (client *Client) DeleteIDPAccount(id string) error {

body, err := EncodeToJSON(map[string]interface{}{"id": id})

if err != nil {
return err
}

opts := RequestOptions{
Path: getAPIEndpoint(false),
Method: "DELETE",
Body: body,
}

_, err = client.RequestAPI(&opts)

if err != nil {
return err
}

return nil
}

// get all idps
Expand Down Expand Up @@ -115,6 +280,22 @@ func (client *Client) GetAccountIDPs() (*[]IDP, error) {
return &idps, nil
}

func (client *Client) GetAccountIdpByID(idpID string) (*IDP, error) {

idpList, err := client.GetAccountIDPs()
if err != nil {
return nil, err
}

for _, idp := range *idpList {
if idp.ID == idpID {
return &idp, nil
}
}

return nil, errors.New(fmt.Sprintf("[ERROR] IDP with ID %s isn't found.", idpID))
}

// add account to idp
func (client *Client) AddAccountToIDP(accountId, idpId string) error {

Expand Down
101 changes: 101 additions & 0 deletions codefresh/data_account_idp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package codefresh

import (
"fmt"

"github.com/codefresh-io/terraform-provider-codefresh/codefresh/cfclient"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func dataSourceAccountIdp() *schema.Resource {
return &schema.Resource{
Description: "This data source retrieves an account level identity provider",
Read: dataSourceAccountIdpRead,
Schema: AccountIdpSchema(),
}
}

// IdpSchema -
func AccountIdpSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"_id": {
Type: schema.TypeString,
Optional: true,
ExactlyOneOf: []string{"_id", "client_name"},
},
"client_name": {
Type: schema.TypeString,
Optional: true,
ExactlyOneOf: []string{"_id", "client_name"},
},
"display_name": {
Type: schema.TypeString,
Computed: true,
},
"client_type": {
Type: schema.TypeString,
Computed: true,
},
"redirect_url": {
Description: "API Callback url for the identity provider",
Type: schema.TypeString,
Computed: true,
},
"redirect_ui_url": {
Description: "UI Callback url for the identity provider",
Type: schema.TypeString,
Computed: true,
},
"login_url": {
Description: "Login url using the IDP to Codefresh",
Type: schema.TypeString,
Computed: true,
},
}
}

func dataSourceAccountIdpRead(d *schema.ResourceData, meta interface{}) error {

client := meta.(*cfclient.Client)

idps, err := client.GetAccountIDPs()
if err != nil {
return err
}

_id, _idOk := d.GetOk("_id")
clientName, clientNameOk := d.GetOk("client_name")

for _, idp := range *idps {
if clientNameOk && clientName.(string) != idp.ClientName {
continue
}
if _idOk && _id.(string) != idp.ID {
continue
}

err = mapDataAccountIdpToResource(idp, d)
if err != nil {
return err
}
}

if d.Id() == "" {
return fmt.Errorf("[EROOR] Idp wasn't found")
}

return nil
}

func mapDataAccountIdpToResource(cfClientIDP cfclient.IDP, d *schema.ResourceData) error {

d.SetId(cfClientIDP.ID)
d.Set("client_name", cfClientIDP.ClientName)
d.Set("client_type", cfClientIDP.ClientType)
d.Set("display_name", cfClientIDP.DisplayName)
d.Set("redirect_url", cfClientIDP.RedirectUrl)
d.Set("redirect_ui_url", cfClientIDP.RedirectUiUrl)
d.Set("login_url", cfClientIDP.LoginUrl)

return nil
}
2 changes: 2 additions & 0 deletions codefresh/internal/idp/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package idp is shared by idp-related resources.
package idp
Loading
Loading