Skip to content

Commit 44cd0e2

Browse files
Feat: Add codefresh_idp and codefresh_account_idp resource and codefresh_account_idp datasource (#138)
## What Add codefresh_idp and codefresh_account_idp resource and codefresh_account_idp datasource ## Why New resources for identity providers ## Notes <!-- Add any notes here --> ## Checklist * [ ] _I have read [CONTRIBUTING.md](https://github.com/codefresh-io/terraform-provider-codefresh/blob/master/CONTRIBUTING.md)._ * [ ] _I have [allowed changes to my fork to be made](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork)._ * [ ] _I have added tests, assuming new tests are warranted_. * [ ] _I understand that the `/test` comment will be ignored by the CI trigger [unless it is made by a repo admin or collaborator](https://codefresh.io/docs/docs/pipelines/triggers/git-triggers/#support-for-building-pull-requests-from-forks)._ --------- Co-authored-by: Yonatan Koren <[email protected]>
1 parent c2313c5 commit 44cd0e2

17 files changed

+2549
-39
lines changed

codefresh/cfclient/idp.go

+207-26
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,200 @@ package cfclient
33
import (
44
"errors"
55
"fmt"
6+
"log"
7+
"net/url"
68
)
79

810
type IDP struct {
9-
Access_token string `json:"access_token,omitempty"`
10-
Accounts []string `json:"accounts,omitempty"`
11-
ApiHost string `json:"apiHost,omitempty"`
12-
ApiPathPrefix string `json:"apiPathPrefix,omitempty"`
13-
ApiURL string `json:"apiURL,omitempty"`
14-
AppId string `json:"appId,omitempty"`
15-
AuthURL string `json:"authURL,omitempty"`
16-
ClientHost string `json:"clientHost,omitempty"`
17-
ClientId string `json:"clientId,omitempty"`
18-
ClientName string `json:"clientName,omitempty"`
19-
ClientSecret string `json:"clientSecret,omitempty"`
20-
ClientType string `json:"clientType,omitempty"`
21-
CookieIv string `json:"cookieIv,omitempty"`
22-
CookieKey string `json:"cookieKey,omitempty"`
23-
DisplayName string `json:"displayName,omitempty"`
24-
ID string `json:"_id,omitempty"`
25-
IDPLoginUrl string `json:"IDPLoginUrl,omitempty"`
26-
LoginUrl string `json:"loginUrl,omitempty"`
27-
RedirectUiUrl string `json:"redirectUiUrl,omitempty"`
28-
RedirectUrl string `json:"redirectUrl,omitempty"`
29-
RefreshTokenURL string `json:"refreshTokenURL,omitempty"`
30-
Scopes []string `json:"scopes,omitempty"`
31-
Tenant string `json:"tenant,omitempty"`
32-
TokenSecret string `json:"tokenSecret,omitempty"`
33-
TokenURL string `json:"tokenURL,omitempty"`
34-
UserProfileURL string `json:"userProfileURL,omitempty"`
11+
ID string `json:"_id,omitempty"`
12+
Access_token string `json:"access_token,omitempty"`
13+
Accounts []string `json:"accounts,omitempty"`
14+
ClientName string `json:"clientName,omitempty"` // IDP name
15+
ClientType string `json:"clientType,omitempty"` // IDP type
16+
DisplayName string `json:"displayName,omitempty"`
17+
LoginUrl string `json:"loginUrl,omitempty"` // Login url in Codefresh
18+
RedirectUiUrl string `json:"redirectUiUrl,omitempty"` // Redicrect url Codefresh UI
19+
RedirectUrl string `json:"redirectUrl,omitempty"`
20+
ClientId string `json:"clientId,omitempty"` // All providers (base)
21+
ClientSecret string `json:"clientSecret,omitempty"` // All providers (base)
22+
ApiHost string `json:"apiHost,omitempty"` // GitHub
23+
ApiPathPrefix string `json:"apiPathPrefix,omitempty"` // Github
24+
// Bitbucket, Gitlab
25+
ApiURL string `json:"apiURL,omitempty"`
26+
// Azure, Okta, onelogin,saml
27+
AppId string `json:"appId,omitempty"`
28+
// Github, Gitlab
29+
AuthURL string `json:"authURL,omitempty"`
30+
// saml, okta, onelogin, auth0, azure, google, google-cloud-sr
31+
ClientHost string `json:"clientHost,omitempty"`
32+
// Azure
33+
CookieIv string `json:"cookieIv,omitempty"`
34+
// Azure
35+
CookieKey string `json:"cookieKey,omitempty"`
36+
// Azure
37+
IDPLoginUrl string `json:"IDPLoginUrl,omitempty"`
38+
// Bitbucket
39+
RefreshTokenURL string `json:"refreshTokenURL,omitempty"`
40+
// Multiple - computed
41+
Scopes []string `json:"scopes,omitempty"`
42+
// Azure
43+
Tenant string `json:"tenant,omitempty"`
44+
TokenSecret string `json:"tokenSecret,omitempty"`
45+
// Okta, Bitbucket, GitHub, Keycloak
46+
TokenURL string `json:"tokenURL,omitempty"`
47+
// Github, Gitlab
48+
UserProfileURL string `json:"userProfileURL,omitempty"`
49+
// Okta
50+
SyncMirrorAccounts []string `json:"syncMirrorAccounts,omitempty"`
51+
// Google, Ldap
52+
AllowedGroupsForSync string `json:"allowedGroupsForSync,omitempty"`
53+
// Google
54+
Subject string `json:"subject,omitempty"`
55+
// Google
56+
KeyFile string `json:"keyfile,omitempty"`
57+
// Google
58+
SyncField string `json:"syncField,omitempty"`
59+
// Azure
60+
AutoGroupSync bool `json:"autoGroupSync,omitempty"`
61+
// Google,Okta,saml
62+
ActivateUserAfterSync bool `json:"activateUserAfterSync,omitempty"`
63+
// Azure
64+
SyncInterval string `json:"syncInterval,omitempty"`
65+
// Onelogin
66+
ApiClientId string `json:"apiClientId,omitempty"`
67+
// Onelogin
68+
ApiClientSecret string `json:"apiClientSecret,omitempty"`
69+
// Keycloak
70+
Host string `json:"host,omitempty"`
71+
// keycloak
72+
Realm string `json:"realm,omitempty"`
73+
// SAML
74+
EntryPoint string `json:"entryPoint,omitempty"`
75+
// SAML
76+
ApplicationCert string `json:"cert,omitempty"`
77+
// SAML
78+
SamlProvider string `json:"provider,omitempty"`
79+
// ldap
80+
Password string `json:"password,omitempty"`
81+
Url string `json:"url,omitempty"`
82+
DistinguishedName string `json:"distinguishedName,omitempty"`
83+
SearchBase string `json:"searchBase,omitempty"`
84+
SearchFilter string `json:"searchFilter,omitempty"`
85+
SearchBaseForSync string `json:"searchBaseForSync,omitempty"`
86+
Certificate string `json:"certificate,omitempty"`
87+
}
88+
89+
// Return the appropriate API endpoint for platform and account scoped IDPs
90+
func getAPIEndpoint(isGlobal bool) string {
91+
// If IDP is platform scoped
92+
if isGlobal {
93+
return "/admin/idp"
94+
} else {
95+
return "/idp/account"
96+
}
97+
}
98+
99+
// 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
100+
// For now we are disabling response decode and in the resource will instead call the read function again
101+
func (client *Client) CreateIDP(idp *IDP, isGlobal bool) (id string, err error) {
102+
103+
body, err := EncodeToJSON(idp)
104+
105+
if err != nil {
106+
return "", err
107+
}
108+
opts := RequestOptions{
109+
Path: getAPIEndpoint(isGlobal),
110+
Method: "POST",
111+
Body: body,
112+
}
113+
114+
resp, err := client.RequestAPI(&opts)
115+
116+
if err != nil {
117+
log.Printf("[DEBUG] Call to API for IDP creation failed with Error = %v for Body %v", err, body)
118+
return "", err
119+
}
120+
121+
var respIDP map[string]interface{}
122+
err = DecodeResponseInto(resp, &respIDP)
123+
124+
if err != nil {
125+
return "", nil
126+
}
127+
128+
return respIDP["id"].(string), nil
129+
}
130+
131+
// Currently on update the API returns a different structure for accounts than on read making the client crash on decode
132+
// For now we are disabling response decode and in the resource will instead call the read function again
133+
func (client *Client) UpdateIDP(idp *IDP, isGlobal bool) error {
134+
135+
body, err := EncodeToJSON(idp)
136+
137+
if err != nil {
138+
return err
139+
}
140+
opts := RequestOptions{
141+
Path: getAPIEndpoint(isGlobal),
142+
Method: "PUT",
143+
Body: body,
144+
}
145+
146+
_, err = client.RequestAPI(&opts)
147+
148+
if err != nil {
149+
log.Printf("[DEBUG] Call to API for IDP update failed with Error = %v for Body %v", err, body)
150+
return err
151+
}
152+
153+
// var respIDP IDP
154+
// err = DecodeResponseInto(resp, &respIDP)
155+
// if err != nil {
156+
// return nil, err
157+
// }
158+
159+
return nil
160+
}
161+
162+
func (client *Client) DeleteIDP(id string) error {
163+
baseUrl := getAPIEndpoint(true)
164+
fullPath := fmt.Sprintf("%s/%s", baseUrl, url.PathEscape(id))
165+
opts := RequestOptions{
166+
Path: fullPath,
167+
Method: "DELETE",
168+
}
169+
170+
_, err := client.RequestAPI(&opts)
171+
172+
if err != nil {
173+
return err
174+
}
175+
176+
return nil
177+
}
178+
179+
func (client *Client) DeleteIDPAccount(id string) error {
180+
181+
body, err := EncodeToJSON(map[string]interface{}{"id": id})
182+
183+
if err != nil {
184+
return err
185+
}
186+
187+
opts := RequestOptions{
188+
Path: getAPIEndpoint(false),
189+
Method: "DELETE",
190+
Body: body,
191+
}
192+
193+
_, err = client.RequestAPI(&opts)
194+
195+
if err != nil {
196+
return err
197+
}
198+
199+
return nil
35200
}
36201

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

283+
func (client *Client) GetAccountIdpByID(idpID string) (*IDP, error) {
284+
285+
idpList, err := client.GetAccountIDPs()
286+
if err != nil {
287+
return nil, err
288+
}
289+
290+
for _, idp := range *idpList {
291+
if idp.ID == idpID {
292+
return &idp, nil
293+
}
294+
}
295+
296+
return nil, errors.New(fmt.Sprintf("[ERROR] IDP with ID %s isn't found.", idpID))
297+
}
298+
118299
// add account to idp
119300
func (client *Client) AddAccountToIDP(accountId, idpId string) error {
120301

codefresh/data_account_idp.go

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package codefresh
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/codefresh-io/terraform-provider-codefresh/codefresh/cfclient"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8+
)
9+
10+
func dataSourceAccountIdp() *schema.Resource {
11+
return &schema.Resource{
12+
Description: "This data source retrieves an account level identity provider",
13+
Read: dataSourceAccountIdpRead,
14+
Schema: AccountIdpSchema(),
15+
}
16+
}
17+
18+
// IdpSchema -
19+
func AccountIdpSchema() map[string]*schema.Schema {
20+
return map[string]*schema.Schema{
21+
"_id": {
22+
Type: schema.TypeString,
23+
Optional: true,
24+
ExactlyOneOf: []string{"_id", "client_name"},
25+
},
26+
"client_name": {
27+
Type: schema.TypeString,
28+
Optional: true,
29+
ExactlyOneOf: []string{"_id", "client_name"},
30+
},
31+
"display_name": {
32+
Type: schema.TypeString,
33+
Computed: true,
34+
},
35+
"client_type": {
36+
Type: schema.TypeString,
37+
Computed: true,
38+
},
39+
"redirect_url": {
40+
Description: "API Callback url for the identity provider",
41+
Type: schema.TypeString,
42+
Computed: true,
43+
},
44+
"redirect_ui_url": {
45+
Description: "UI Callback url for the identity provider",
46+
Type: schema.TypeString,
47+
Computed: true,
48+
},
49+
"login_url": {
50+
Description: "Login url using the IDP to Codefresh",
51+
Type: schema.TypeString,
52+
Computed: true,
53+
},
54+
}
55+
}
56+
57+
func dataSourceAccountIdpRead(d *schema.ResourceData, meta interface{}) error {
58+
59+
client := meta.(*cfclient.Client)
60+
61+
idps, err := client.GetAccountIDPs()
62+
if err != nil {
63+
return err
64+
}
65+
66+
_id, _idOk := d.GetOk("_id")
67+
clientName, clientNameOk := d.GetOk("client_name")
68+
69+
for _, idp := range *idps {
70+
if clientNameOk && clientName.(string) != idp.ClientName {
71+
continue
72+
}
73+
if _idOk && _id.(string) != idp.ID {
74+
continue
75+
}
76+
77+
err = mapDataAccountIdpToResource(idp, d)
78+
if err != nil {
79+
return err
80+
}
81+
}
82+
83+
if d.Id() == "" {
84+
return fmt.Errorf("[EROOR] Idp wasn't found")
85+
}
86+
87+
return nil
88+
}
89+
90+
func mapDataAccountIdpToResource(cfClientIDP cfclient.IDP, d *schema.ResourceData) error {
91+
92+
d.SetId(cfClientIDP.ID)
93+
d.Set("client_name", cfClientIDP.ClientName)
94+
d.Set("client_type", cfClientIDP.ClientType)
95+
d.Set("display_name", cfClientIDP.DisplayName)
96+
d.Set("redirect_url", cfClientIDP.RedirectUrl)
97+
d.Set("redirect_ui_url", cfClientIDP.RedirectUiUrl)
98+
d.Set("login_url", cfClientIDP.LoginUrl)
99+
100+
return nil
101+
}

codefresh/internal/idp/doc.go

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package idp is shared by idp-related resources.
2+
package idp

0 commit comments

Comments
 (0)