diff --git a/client/config.go b/client/config.go index 12c3edf..5b588d7 100644 --- a/client/config.go +++ b/client/config.go @@ -1,6 +1,9 @@ package client import ( + "encoding/json" + "log" + "github.com/BESTSELLER/terraform-provider-harbor/models" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -14,18 +17,55 @@ func GetConfigSystem(d *schema.ResourceData) models.ConfigBodyPost { } func GetConfigAuth(d *schema.ResourceData) models.ConfigBodyPost { - return models.ConfigBodyPost{ - AuthMode: d.Get("auth_mode").(string), - OidcName: d.Get("oidc_name").(string), - OidcEndpoint: d.Get("oidc_endpoint").(string), - OidcClientID: d.Get("oidc_client_id").(string), - OidcClientSecret: d.Get("oidc_client_secret").(string), - OidcGroupsClaim: d.Get("oidc_groups_claim").(string), - OidcScope: d.Get("oidc_scope").(string), - OidcVerifyCert: d.Get("oidc_verify_cert").(bool), - OidcAutoOnboard: d.Get("oidc_auto_onboard").(bool), - OidcUserClaim: d.Get("oidc_user_claim").(string), + var body models.ConfigBodyPost + + auth := d.Get("auth_mode").(string) + + switch auth { + case "oidc_auth", "oidc": + body = models.ConfigBodyPost{ + AuthMode: "oidc_auth", + OidcName: d.Get("oidc_name").(string), + OidcEndpoint: d.Get("oidc_endpoint").(string), + OidcClientID: d.Get("oidc_client_id").(string), + OidcClientSecret: d.Get("oidc_client_secret").(string), + OidcGroupsClaim: d.Get("oidc_groups_claim").(string), + OidcScope: d.Get("oidc_scope").(string), + OidcVerifyCert: d.Get("oidc_verify_cert").(bool), + OidcAutoOnboard: d.Get("oidc_auto_onboard").(bool), + OidcUserClaim: d.Get("oidc_user_claim").(string), + } + case "ldap_auth", "ldap": + body = models.ConfigBodyPost{ + AuthMode: "ldap_auth", + LdapURL: d.Get("ldap_url").(string), + LdapSearchDn: d.Get("ldap_search_dn").(string), + LdapBaseDn: d.Get("ldap_base_dn").(string), + LdapFilter: d.Get("ldap_filter").(string), + LdapUID: d.Get("ldap_uid").(string), + + LdapGroupBaseDn: d.Get("ldap_group_base_dn").(string), + LdapGroupSearchFilter: d.Get("ldap_group_filter").(string), + LdapGroupGID: d.Get("ldap_group_gid").(string), + LdapGroupAdminDn: d.Get("ldap_group_admin_dn").(string), + LdapGroupAttributeName: d.Get("ldap_group_membership").(string), + + LdapVerifyCert: d.Get("ldap_verify_cert").(bool), + } + + ldapScope := d.Get("ldap_scope").(string) + if ldapScope != "" { + body.LdapScope = getLdapScope(ldapScope) + } + + ldapGroupScope := d.Get("ldap_group_scope").(string) + if ldapGroupScope != "" { + body.LdapGroupSearchScope = getLdapScope(ldapGroupScope) + } + } + log.Printf("[DEBUG] %+v\n ", body) + return body } func GetConfigEmail(d *schema.ResourceData) models.ConfigBodyPost { @@ -44,3 +84,55 @@ func days2mins(days int) int { mins := days * 1440 return mins } + +func getLdapScope(scopeName string) (scopeInt int) { + var scope int + switch scopeName { + case "base": + scope = 0 + case "onelevel": + scope = 1 + case "subtree": + scope = 2 + } + return scope +} + +// SetAuthValues the values for Auth when performing a read on resource +func SetAuthValues(d *schema.ResourceData, resp string) error { + var jsonData models.ConfigBodyResponse + err := json.Unmarshal([]byte(resp), &jsonData) + if err != nil { + return err + } + + auth := jsonData.AuthMode.Value + d.Set("auth_mode", auth) + + switch auth { + case "oidc_auth", "oidc": + d.Set("oidc_name", jsonData.OidcName.Value) + d.Set("oidc_endpoint", jsonData.OidcEndpoint.Value) + d.Set("oidc_client_id", jsonData.OidcClientID.Value) + d.Set("oidc_groups_claim", jsonData.OidcGroupsClaim.Value) + d.Set("oidc_scope", jsonData.OidcScope.Value) + d.Set("oidc_verify_cert", jsonData.OidcVerifyCert) + d.Set("oidc_auto_onboard", jsonData.OidcAutoOnboard) + d.Set("oidc_user_claim", jsonData.OidcUserClaim) + case "ldap_auth", "ldap": + d.Set("ldap_url", jsonData.LdapURL.Value) + d.Set("ldap_base_dn", jsonData.LdapBaseDn.Value) + d.Set("ldap_uid", jsonData.LdapUID.Value) + d.Set("ldap_verify_cert", jsonData.VerifyRemoteCert.Value) + d.Set("ldap_search_dn", jsonData.LdapSearchDn.Value) + d.Set("ldap_scope", jsonData.LdapScope.Value) + d.Set("ldap_group_base_dn", jsonData.LdapGroupBaseDn.Value) + d.Set("ldap_group_filter", jsonData.LdapGroupSearchFilter) + d.Set("ldap_group_gid", jsonData.LdapGroupAttributeName.Value) + d.Set("ldap_group_admin_dn", jsonData.LdapGroupAdminDn) + d.Set("ldap_group_membership", jsonData.LdapGroupMembershipAttribute) + d.Set("ldap_group_scope", jsonData.LdapGroupSearchScope) + } + + return nil +} diff --git a/docs/resources/configuration.md b/docs/resources/configuration.md index 75db1ef..d763fb1 100644 --- a/docs/resources/configuration.md +++ b/docs/resources/configuration.md @@ -1,7 +1,7 @@ # Resource: harbor_configuration ## Example Usage - +How to configure oidc ```hcl resource "harbor_config_auth" "oidc" { auth_mode = "oidc_auth" @@ -16,27 +16,57 @@ resource "harbor_config_auth" "oidc" { } ``` +How to configure ldap +```hcl +resource "harbor_config_auth" "ldap" { + auth_mode = "ldap_auth" + ldap_url = "openldap.default.svc.cluster.local:389" + ldap_search_dn = "cn=admin,dc=example,dc=org" + ldap_search_password = "Not@SecurePassw0rd" + ldap_base_dn = "dc=example,dc=org" + ldap_uid = "email" + ldap_verify_cert = false +} +``` + ## Argument Reference The following arguments are supported: -* **auth_mode** - (Required) Harbor authentication mode. Can be **"oidc_auth"** or **"db_auth"**. (Default: **"db_auth"**) +* `auth_mode` - (Required) Harbor authentication mode. Can be `"oidc_auth"`, `"db_auth"` or `"ldap_auth"`. (Default: **"db_auth"**) -* **oidc_name** - (Optional) The name of the oidc provider name. (Required - if auth_mode set to **oidc_auth**) +* `oidc_name` - (Optional) The name of the oidc provider name. (Required - if auth_mode set to **oidc_auth**) -* **oidc_endpoint** - (Optional) The URL of an OIDC-complaint server. (Required - if auth_mode set to **oidc_auth**) +* `oidc_endpoint` - (Optional) The URL of an OIDC-complaint server. (Required - if auth_mode set to **oidc_auth**) -* **oidc_client_id** - (Optional) The client id for the oidc server. (Required - if auth_mode set to **oidc_auth**) +* `oidc_client_id` - (Optional) The client id for the oidc server. (Required - if auth_mode set to **oidc_auth**) -* **oidc_client_serect** - (Optional) The client secert for the oidc server. (Required - if auth_mode set to **oidc_auth**) +* `oidc_client_serect` - (Optional) The client secert for the oidc server. (Required - if auth_mode set to **oidc_auth**) -* **oidc_groups_claim** - (Optional) The name of the claim in the token whose values is the list of group names. +* `oidc_groups_claim` - (Optional) The name of the claim in the token whose values is the list of group names. `NOTE: "oidc_groups_claim" can only be used with harbor version v1.10.1 and above` -* **oidc_scope** - (Optional) The scope sent to OIDC server during authentication. It has to contain “openid”. (Required - if auth_mode set to **oidc_auth**) +* `oidc_scope` - (Optional) The scope sent to OIDC server during authentication. It has to contain “openid”. (Required - if auth_mode set to **oidc_auth**) + +* `oidc_verify_cert` - (Optional) Set to **"false"** if your OIDC server is using a self-signed certificate. (Required - if auth_mode set to **oidc_auth**) + +* `oidc_auto_onboard` - (Optional) Default is **"false"**, set to **"true"** if you want to skip the user onboarding screen, so user cannot change its username -* **oidc_verify_cert** - (Optional) Set to **"false"** if your OIDC server is using a self-signed certificate. (Required - if auth_mode set to **oidc_auth**) +* `idc_user_claim` - (Optional) Default is **"name"** -* **oidc_auto_onboard** - (Optional) Default is **"false"**, set to **"true"** if you want to skip the user onboarding screen, so user cannot change its username -* **oidc_user_claim** - (Optional) Default is **"name"** \ No newline at end of file +* `ldap_url` - (Optional) The ldap server. Required when auth_mode is set to ldap. +* `ldap_base_dn` - (Optional) A user's DN who has the permission to search the LDAP/AD server. +* `ldap_uid`- (Optional) The attribute used in a search to match a user. It could be uid, cn, email, sAMAccountName or other attributes depending on your LDAP/AD. +* `ldap_verify_cert`- (Optional) Verify Cert from LDAP Server. +* `ldap_search_dn` - (Optional) The base DN from which to look up a user in LDAP/AD. +* `ldap_search_password` - (Optional) The password for the user that will perform the LDAP search +* `ldap_filter` - (Optional) ldap filters +* `ldap_scope` - (Optional) LDAP Group Scope +* `ldap_group_base_dn` - (Optional) The base DN from which to look up a group in LDAP/AD. +* `ldap_group_filter` - (Optional) The filter to look up an LDAP/AD group. +* `ldap_group_gid` - (Optional) - The attribute used in a search to match a user +* `ldap_group_admin_dn` - (Optional) Specify an LDAP group DN. All LDAP user in this group will have harbor admin privilege +* `ldap_group_membership` - (Optional) The attribute indicates the membership of LDAP group +* `ldap_group_scope` - (Optional) The scope to search for groups + \ No newline at end of file diff --git a/models/config.go b/models/config.go index 0edb1b1..3d1bd8f 100644 --- a/models/config.go +++ b/models/config.go @@ -47,6 +47,9 @@ type ConfigBodyPost struct { EmailHost string `json:"email_host,omitempty"` EmailFrom string `json:"email_from,omitempty"` RobotTokenDuration int `json:"robot_token_duration,omitempty"` + LdapVerifyCert bool `json:"ldap_verify_cert,omitempty"` + + LdapGroupGID string `json:"ldap_group_id,omitempty"` } type ConfigBodyResponse struct { @@ -62,6 +65,10 @@ type ConfigBodyResponse struct { Editable bool `json:"editable,omitempty"` Value string `json:"value,omitempty"` } `json:"oidc_user_claim,omitempty"` + OidcGroupsClaim struct { + Editable bool `json:"editable,omitempty"` + Value string `json:"value,omitempty"` + } `json:"oidc_groups_claim,omitempty"` EmailIdentity struct { Editable bool `json:"editable,omitempty"` Value string `json:"value,omitempty"` @@ -156,6 +163,10 @@ type ConfigBodyResponse struct { Editable bool `json:"editable,omitempty"` Value string `json:"value,omitempty"` } `json:"ldap_group_admin_dn,omitempty"` + LdapGroupMembershipAttribute struct { + Editable bool `json:"editable,omitempty"` + Value string `json:"value,omitempty"` + } `json:"ldap_group_membership_attribute,omitempty"` EmailUsername struct { Editable bool `json:"editable,omitempty"` Value string `json:"value,omitempty"` diff --git a/provider/resource_config_auth.go b/provider/resource_config_auth.go index faa9f81..de06f70 100644 --- a/provider/resource_config_auth.go +++ b/provider/resource_config_auth.go @@ -14,39 +14,126 @@ func resourceConfigAuth() *schema.Resource { Required: true, }, "oidc_name": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + RequiredWith: oidcRequiredWith(), + ConflictsWith: oidcConflictsWith(), }, "oidc_endpoint": { + Type: schema.TypeString, + Optional: true, + RequiredWith: oidcRequiredWith(), + ConflictsWith: oidcConflictsWith(), + }, + "oidc_client_id": { + Type: schema.TypeString, + Optional: true, + RequiredWith: oidcRequiredWith(), + ConflictsWith: oidcConflictsWith(), + }, + "oidc_client_secret": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + RequiredWith: oidcRequiredWith(), + ConflictsWith: oidcConflictsWith(), + }, + "oidc_groups_claim": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: oidcConflictsWith(), + }, + "oidc_scope": { + Type: schema.TypeString, + Optional: true, + RequiredWith: oidcRequiredWith(), + ConflictsWith: oidcConflictsWith(), + }, + "oidc_verify_cert": { + Type: schema.TypeBool, + Optional: true, + ConflictsWith: oidcConflictsWith(), + }, + "oidc_auto_onboard": { + Type: schema.TypeBool, + Optional: true, + ConflictsWith: oidcConflictsWith(), + }, + "oidc_user_claim": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: oidcConflictsWith(), + }, + "ldap_url": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: ldapConflictsWith(), + RequiredWith: ldapRequiredWith(), + }, + "ldap_base_dn": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: ldapConflictsWith(), + RequiredWith: ldapRequiredWith(), + }, + "ldap_uid": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: ldapConflictsWith(), + RequiredWith: ldapRequiredWith(), + }, + + "ldap_verify_cert": { + Type: schema.TypeBool, + Optional: true, + ConflictsWith: ldapConflictsWith(), + RequiredWith: ldapRequiredWith(), + }, + "ldap_search_dn": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: ldapConflictsWith(), + RequiredWith: ldapRequiredWith(), + }, + "ldap_search_password": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + ConflictsWith: ldapConflictsWith(), + RequiredWith: ldapRequiredWith(), + }, + "ldap_filter": { Type: schema.TypeString, Optional: true, }, - "oidc_client_id": { + "ldap_group_uid": { Type: schema.TypeString, Optional: true, }, - "oidc_client_secret": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, + "ldap_scope": { + Type: schema.TypeString, + Optional: true, }, - "oidc_groups_claim": { + "ldap_group_base_dn": { Type: schema.TypeString, Optional: true, }, - "oidc_scope": { + "ldap_group_filter": { Type: schema.TypeString, Optional: true, }, - "oidc_verify_cert": { - Type: schema.TypeBool, + "ldap_group_gid": { + Type: schema.TypeString, Optional: true, }, - "oidc_auto_onboard": { - Type: schema.TypeBool, + "ldap_group_admin_dn": { + Type: schema.TypeString, + Optional: true}, + "ldap_group_membership": { + Type: schema.TypeString, Optional: true, }, - "oidc_user_claim": { + "ldap_group_scope": { Type: schema.TypeString, Optional: true, }, @@ -72,23 +159,52 @@ func resourceConfigAuthCreate(d *schema.ResourceData, m interface{}) error { } func resourceConfigAuthRead(d *schema.ResourceData, m interface{}) error { + apiClient := m.(*client.Client) + + resp, _, err := apiClient.SendRequest("GET", models.PathConfig, nil, 200) + if err != nil { + return err + } - d.SetId("configuration/auth") + err = client.SetAuthValues(d, resp) + if err != nil { + return err + } + + d.SetId(models.PathConfig) return nil } -// func resourceConfigAuthUpdate(d *schema.ResourceData, m interface{}) error { -// apiClient := m.(*client.Client) -// body := client.GetConfigAuth(d) +func resourceConfigAuthDelete(d *schema.ResourceData, m interface{}) error { + return nil +} -// _, _, err := apiClient.SendRequest("PUT", models.PathConfig, body, 200) -// if err != nil { -// return err -// } +func oidcConflictsWith() []string { + return []string{"ldap_url", + "ldap_base_dn", + "ldap_uid", + "ldap_verify_cert", + "ldap_search_dn", + "ldap_search_password", + "ldap_filter", + "ldap_group_uid", + "ldap_scope", + "ldap_group_base_dn", + "ldap_group_filter", + "ldap_group_gid", + "ldap_group_admin_dn", + "ldap_group_membership", + "ldap_group_scope"} +} -// return resourceConfigAuthRead(d, m) -// } +func oidcRequiredWith() []string { + return []string{"oidc_name", "oidc_endpoint", "oidc_client_id", "oidc_client_secret", "oidc_scope"} +} -func resourceConfigAuthDelete(d *schema.ResourceData, m interface{}) error { - return nil +func ldapConflictsWith() []string { + return []string{"oidc_name", "oidc_endpoint", "oidc_client_id", "oidc_client_secret", "oidc_groups_claim", "oidc_scope", "oidc_verify_cert", "oidc_auto_onboard", "oidc_user_claim"} +} + +func ldapRequiredWith() []string { + return []string{"ldap_url", "ldap_base_dn", "ldap_uid", "ldap_verify_cert"} } diff --git a/provider/resource_robot_account.go b/provider/resource_robot_account.go index 99eb7c4..394cfcb 100644 --- a/provider/resource_robot_account.go +++ b/provider/resource_robot_account.go @@ -112,6 +112,7 @@ func resourceRobotAccountRead(d *schema.ResourceData, m interface{}) error { return fmt.Errorf("Resource not found %s", d.Id()) } + d.Set("project_id", "/projects/"+strconv.Itoa(jsonData.ProjectID)) d.Set("robot_id", strconv.Itoa(jsonData.ID)) d.Set("name", strings.Replace(jsonData.Name, "robot$", "", -1)) d.Set("description", jsonData.Description)