diff --git a/client/account.go b/client/account.go index a8947213..e2e63899 100644 --- a/client/account.go +++ b/client/account.go @@ -217,12 +217,12 @@ func (client *Client) UpdateAccount(account *Account) (*Account, error) { } existingAccount, err := client.GetAccountByID(id) - if err != nil { + if err != nil { return nil, err } err = mergo.Merge(account, existingAccount) - if err != nil { + if err != nil { return nil, err } diff --git a/client/idp.go b/client/idp.go new file mode 100644 index 00000000..f730b32c --- /dev/null +++ b/client/idp.go @@ -0,0 +1,139 @@ +package client + +import ( + "errors" + "fmt" +) + +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"` +} + +// get all idps +func (client *Client) GetIDPs() (*[]IDP, error) { + fullPath := "/admin/idp" + opts := RequestOptions{ + Path: fullPath, + Method: "GET", + } + + resp, err := client.RequestAPI(&opts) + + if err != nil { + return nil, err + } + + var idps []IDP + + err = DecodeResponseInto(resp, &idps) + if err != nil { + return nil, err + } + + return &idps, nil +} + +// get idp id by idp name +func (client *Client) GetIdpByName(idpName string) (*IDP, error) { + + idpList, err := client.GetIDPs() + if err != nil { + return nil, err + } + + for _, idp := range *idpList { + if idp.ClientName == idpName { + return &idp, nil + } + } + + return nil, errors.New(fmt.Sprintf("[ERROR] IDP with name %s isn't found.", idpName )) +} + +func (client *Client) GetIdpByID(idpID string) (*IDP, error) { + + idpList, err := client.GetIDPs() + 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)) +} + + +// get account idps +func (client *Client) GetAccountIDPs() (*[]IDP, error) { + fullPath := "/idp/account" + opts := RequestOptions{ + Path: fullPath, + Method: "GET", + } + + resp, err := client.RequestAPI(&opts) + + if err != nil { + return nil, err + } + + var idps []IDP + + err = DecodeResponseInto(resp, &idps) + if err != nil { + return nil, err + } + + return &idps, nil +} + +// add account to idp +func (client *Client) AddAccountToIDP(accountId, idpId string) error { + + body := fmt.Sprintf(`{"accountId":"%s","IDPConfigId":"%s"}`, accountId, idpId) + + opts := RequestOptions{ + Path: "/admin/idp/addAccount", + Method: "POST", + Body: []byte(body), + } + + _, err := client.RequestAPI(&opts) + if err != nil { + return err + } + + return nil +} + +// remove account form idp +// doesn't implemente diff --git a/client/team.go b/client/team.go index 7aa09f33..36fa2f91 100644 --- a/client/team.go +++ b/client/team.go @@ -242,27 +242,18 @@ func GetUsersDiff(desiredUsers []string, existingUsers []TeamUser) (usersToAdd [ } for _, id := range existingUsersIDs { - ok := find(desiredUsers, id) + ok := FindInSlice(desiredUsers, id) if !ok { usersToDelete = append(usersToDelete, id) } } for _, id := range desiredUsers { - ok := find(existingUsersIDs, id) + ok := FindInSlice(existingUsersIDs, id) if !ok { usersToAdd = append(usersToAdd, id) } } return usersToAdd, usersToDelete -} - -func find(slice []string, val string) bool { - for _, item := range slice { - if item == val { - return true - } - } - return false -} +} \ No newline at end of file diff --git a/client/user.go b/client/user.go index a0f22422..bb23c3c2 100644 --- a/client/user.go +++ b/client/user.go @@ -2,25 +2,52 @@ package client import "fmt" +type Credentials struct { + Permissions []string `json:"permissions,omitempty"` +} + +type Login struct { + Credentials Credentials `json:"credentials,omitempty"` + PersonalGit bool `json:"personalGit,omitempty"` + Permissions []string `json:"permissions,omitempty"` + IDP IDP `json:"idp,omitempty"` +} + +type ShortProfile struct { + UserName string `json:"userName,omitempty"` +} + +type Personal struct { + FirstName string `json:"firstName,omitempty"` + LastName string `json:"lastName,omitempty"` + CompanyName string `json:"companyName,omitempty"` + PhoneNumber string `json:"phoneNumber,omitempty"` + Country string `json:"country,omitempty"` +} + type User struct { - ID string `json:"_id"` + ID string `json:"_id,omitempty"` UserName string `json:"userName"` Email string `json:"email"` - Roles []interface{} `json:"roles"` - DefaultAccount int `json:"defaultAccount"` - Account []Account `json:"account"` - Status string `json:"status"` - RegisterDate string `json:"register_date"` - HasPassword bool `json:"hasPassword"` - Notifications []NotificationEvent `json:"notifications"` - ShortProfile struct { - UserName string `json:"userName"` - } `json:"shortProfile"` - Settings struct { - SendWeeklyReport bool `json:"sendWeeklyReport"` - } `json:"settings"` - Logins []interface{} `json:"logins"` - InviteURL string `json:"inviteUrl"` + Personal Personal `json:"personal,omitempty"` + Roles []string `json:"roles,omitempty"` + DefaultAccount int `json:"defaultAccount,omitempty"` + Account []Account `json:"account,omitempty"` + Status string `json:"status,omitempty"` + RegisterDate string `json:"register_date,omitempty"` + HasPassword bool `json:"hasPassword,omitempty"` + Notifications []NotificationEvent `json:"notifications,omitempty"` + ShortProfile ShortProfile `json:"shortProfile,omitempty"` + Logins []Login `json:"logins,omitempty"` + InviteURL string `json:"inviteUrl,omitempty"` +} + +type NewUser struct { + UserName string `json:"userName"` + Email string `json:"email"` + Logins []Login `json:"logins,omitempty"` + Roles []string `json:"roles,omitempty"` + Account []string `json:"account,omitempty"` } func (client *Client) AddNewUserToAccount(accountId, userName, userEmail string) (*User, error) { @@ -50,6 +77,33 @@ func (client *Client) AddNewUserToAccount(accountId, userName, userEmail string) return &user, nil } +func (client *Client) AddPendingUser(user *NewUser) (*User, error) { + + body, err := EncodeToJSON(user) + if err != nil { + return nil, err + } + opts := RequestOptions{ + Path: "/admin/accounts/addpendinguser", + Method: "POST", + Body: body, + } + + resp, err := client.RequestAPI(&opts) + if err != nil { + return nil, err + } + + var respUser User + + err = DecodeResponseInto(resp, &respUser) + if err != nil { + return nil, err + } + + return &respUser, nil +} + func (client *Client) ActivateUser(userId string) (*User, error) { opts := RequestOptions{ @@ -86,4 +140,57 @@ func (client *Client) SetUserAsAccountAdmin(accountId, userId string) error { } return nil -} \ No newline at end of file +} + +func (client *Client) DeleteUserAsAccountAdmin(accountId, userId string) error { + + opts := RequestOptions{ + Path: fmt.Sprintf("/accounts/%s/%s/admin", accountId, userId), + Method: "DELETE", + } + + _, err := client.RequestAPI(&opts) + if err != nil { + return err + } + + return nil +} + +func (client *Client) ListUsers() (*[]User, error) { + + opts := RequestOptions{ + Path: "/admin/user", + Method: "GET", + } + + resp, err := client.RequestAPI(&opts) + if err != nil { + return nil, err + } + + var users []User + + err = DecodeResponseInto(resp, &users) + if err != nil { + return nil, err + } + + return &users, nil +} + +func (client *Client) DeleteUser(userName string) error { + + opts := RequestOptions{ + Path: fmt.Sprintf("/admi/user/%s", userName), + Method: "DELETE", + } + + _, err := client.RequestAPI(&opts) + if err != nil { + return err + } + + return nil +} + diff --git a/client/utils.go b/client/utils.go index 0ee5e3ee..60a97c60 100644 --- a/client/utils.go +++ b/client/utils.go @@ -10,3 +10,12 @@ type Variable struct { type CodefreshObject interface { GetID() string } + +func FindInSlice(slice []string, val string) bool { + for _, item := range slice { + if item == val { + return true + } + } + return false +} diff --git a/codefresh/provider.go b/codefresh/provider.go index 6fbf6e32..098dd5d5 100644 --- a/codefresh/provider.go +++ b/codefresh/provider.go @@ -32,6 +32,7 @@ func Provider() terraform.ResourceProvider { "codefresh_team": resourceTeam(), "codefresh_account": resourceAccount(), "codefresh_api_key": resourceApiKey(), + "codefresh_idp_accounts": resourceIDPAccounts(), }, ConfigureFunc: configureProvider, } diff --git a/codefresh/resource_account.go b/codefresh/resource_account.go index e1f00dd5..1c9dd67b 100644 --- a/codefresh/resource_account.go +++ b/codefresh/resource_account.go @@ -112,10 +112,6 @@ func resourceAccountUpdate(d *schema.ResourceData, meta interface{}) error { return err } - // TODO - // - rename account - // - add/remove admins - return nil } diff --git a/codefresh/resource_idp_accounts.go b/codefresh/resource_idp_accounts.go new file mode 100644 index 00000000..43921a87 --- /dev/null +++ b/codefresh/resource_idp_accounts.go @@ -0,0 +1,110 @@ +package codefresh + +import ( + cfClient "github.com/codefresh-io/terraform-provider-codefresh/client" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func resourceIDPAccounts() *schema.Resource { + return &schema.Resource{ + Create: resourceAccountIDPCreate, + Read: resourceAccountIDPRead, + Update: resourceAccountIDPUpdate, + Delete: resourceAccountIDPDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "idp": { + Type: schema.TypeString, + Required: true, + }, + "accounts": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} + +func resourceAccountIDPCreate(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*cfClient.Client) + + accounts:= convertStringArr(d.Get("accounts").(*schema.Set).List()) + + idpName := d.Get("idp").(string) + + idp, err := client.GetIdpByName(idpName) + if err != nil { + return err + } + + for _, account := range accounts { + client.AddAccountToIDP(account, idp.ID) + } + + d.SetId(idp.ID) + + return nil +} + +func resourceAccountIDPRead(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*cfClient.Client) + + idpID := d.Id() + if idpID == "" { + d.SetId("") + return nil + } + + idp, err := client.GetIdpByID(idpID) + if err != nil { + return err + } + + err = d.Set("idp", idp.ClientName) + if err != nil { + return err + } + + err = d.Set("accounts", idp.Accounts) + if err != nil { + return err + } + + return nil +} + + +func resourceAccountIDPDelete(_ *schema.ResourceData, _ interface{}) error { + return nil +} + +func resourceAccountIDPUpdate(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*cfClient.Client) + + idpID := d.Id() + + idp, err := client.GetIdpByID(idpID) + if err != nil { + return err + } + + existingAccounts := idp.Accounts + + desiredAccounts := convertStringArr(d.Get("accounts").(*schema.Set).List()) + + for _, account := range desiredAccounts { + if ok := cfClient.FindInSlice(existingAccounts, account); !ok { + client.AddAccountToIDP(account, idp.ID) + } + } + + return nil +} \ No newline at end of file diff --git a/codefresh/resource_team.go b/codefresh/resource_team.go index 5232ca36..ab9a0def 100644 --- a/codefresh/resource_team.go +++ b/codefresh/resource_team.go @@ -39,7 +39,7 @@ func resourceTeam() *schema.Resource { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ - Type: schema.TypeString,g + Type: schema.TypeString, }, }, }, diff --git a/docs/resources/idp-accounts.md b/docs/resources/idp-accounts.md new file mode 100644 index 00000000..e196bd16 --- /dev/null +++ b/docs/resources/idp-accounts.md @@ -0,0 +1,33 @@ +# IDP Admins resource + +The resource adds the list of provided account IDs to the IDP. +Because of the current Codefresh API limitation it's impossible to remove account from IDP, only adding is supporting. + +## Example usage + +```hcl +resource "codefresh_account" "test" { + name = "" +} + +resource "codefresh_idp_accounts" "test" { + + idp = "azure" + + accounts = [ + codefresh_account.test.id, + "" + ] +} +``` + +## Argument Reference + +- `idp` - (Required) The IDP client name. +- `accounts` - (Required) A list of account IDs. + +## Import + +```sh +terraform import codefresh_idp_accounts.test xxxxxxxxxxxxxxxxxxx +```