diff --git a/CHANGELOG.md b/CHANGELOG.md index f924abd4..fa1b53f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.4.0 (UNRELEASED) +ENHANCEMENTS: + +* provider/configuration: Add `virtual_environment.otp` argument for TOTP support + BUG FIXES: * library/virtual_environment_nodes: Fix node IP address format diff --git a/docs/index.md b/docs/index.md index 34997c58..48f99181 100644 --- a/docs/index.md +++ b/docs/index.md @@ -87,5 +87,6 @@ In addition to [generic provider arguments](https://www.terraform.io/docs/config * `virtual_environment` - (Optional) The Proxmox Virtual Environment configuration. * `endpoint` - (Required) The endpoint for the Proxmox Virtual Environment API (can also be sourced from `PROXMOX_VE_ENDPOINT`). * `insecure` - (Optional) Whether to skip the TLS verification step (can also be sourced from `PROXMOX_VE_INSECURE`). If omitted, defaults to `false`. + * `otp` - (Optional) The one-time password for the Proxmox Virtual Environment API (can also be sourced from `PROXMOX_VE_OTP`). * `password` - (Required) The password for the Proxmox Virtual Environment API (can also be sourced from `PROXMOX_VE_PASSWORD`). * `username` - (Required) The username and realm for the Proxmox Virtual Environment API (can also be sourced from `PROXMOX_VE_USERNAME`). diff --git a/proxmox/virtual_environment_authentication.go b/proxmox/virtual_environment_authentication.go index 5970c70a..e6d17de2 100644 --- a/proxmox/virtual_environment_authentication.go +++ b/proxmox/virtual_environment_authentication.go @@ -24,8 +24,24 @@ func (c *VirtualEnvironmentClient) Authenticate(reset bool) error { return nil } - body := bytes.NewBufferString(fmt.Sprintf("username=%s&password=%s", url.QueryEscape(c.Username), url.QueryEscape(c.Password))) - req, err := http.NewRequest(hmPOST, fmt.Sprintf("%s/%s/access/ticket", c.Endpoint, basePathJSONAPI), body) + var reqBody *bytes.Buffer + + if c.OTP != nil { + reqBody = bytes.NewBufferString(fmt.Sprintf( + "username=%s&password=%s&otp=%s", + url.QueryEscape(c.Username), + url.QueryEscape(c.Password), + url.QueryEscape(*c.OTP), + )) + } else { + reqBody = bytes.NewBufferString(fmt.Sprintf( + "username=%s&password=%s", + url.QueryEscape(c.Username), + url.QueryEscape(c.Password), + )) + } + + req, err := http.NewRequest(hmPOST, fmt.Sprintf("%s/%s/access/ticket", c.Endpoint, basePathJSONAPI), reqBody) if err != nil { return errors.New("Failed to create authentication request") diff --git a/proxmox/virtual_environment_client.go b/proxmox/virtual_environment_client.go index 8b41847e..c6045d39 100644 --- a/proxmox/virtual_environment_client.go +++ b/proxmox/virtual_environment_client.go @@ -21,7 +21,7 @@ import ( ) // NewVirtualEnvironmentClient creates and initializes a VirtualEnvironmentClient instance. -func NewVirtualEnvironmentClient(endpoint, username, password string, insecure bool) (*VirtualEnvironmentClient, error) { +func NewVirtualEnvironmentClient(endpoint, username, password, otp string, insecure bool) (*VirtualEnvironmentClient, error) { url, err := url.ParseRequestURI(endpoint) if err != nil { @@ -40,6 +40,12 @@ func NewVirtualEnvironmentClient(endpoint, username, password string, insecure b return nil, errors.New("You must specify a username for the Proxmox Virtual Environment API") } + var pOTP *string + + if otp != "" { + pOTP = &otp + } + httpClient := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ @@ -51,6 +57,7 @@ func NewVirtualEnvironmentClient(endpoint, username, password string, insecure b return &VirtualEnvironmentClient{ Endpoint: strings.TrimRight(url.String(), "/"), Insecure: insecure, + OTP: pOTP, Password: password, Username: username, httpClient: httpClient, diff --git a/proxmox/virtual_environment_client_types.go b/proxmox/virtual_environment_client_types.go index 4f1e6203..e80e7d94 100644 --- a/proxmox/virtual_environment_client_types.go +++ b/proxmox/virtual_environment_client_types.go @@ -22,6 +22,7 @@ const ( type VirtualEnvironmentClient struct { Endpoint string Insecure bool + OTP *string Password string Username string diff --git a/proxmoxtf/provider.go b/proxmoxtf/provider.go index caaec54e..2bdbfb21 100644 --- a/proxmoxtf/provider.go +++ b/proxmoxtf/provider.go @@ -14,9 +14,15 @@ import ( ) const ( + dvProviderVirtualEnvironmentEndpoint = "" + dvProviderVirtualEnvironmentOTP = "" + dvProviderVirtualEnvironmentPassword = "" + dvProviderVirtualEnvironmentUsername = "" + mkProviderVirtualEnvironment = "virtual_environment" mkProviderVirtualEnvironmentEndpoint = "endpoint" mkProviderVirtualEnvironmentInsecure = "insecure" + mkProviderVirtualEnvironmentOTP = "otp" mkProviderVirtualEnvironmentPassword = "password" mkProviderVirtualEnvironmentUsername = "username" ) @@ -68,7 +74,7 @@ func Provider() *schema.Provider { Description: "The endpoint for the Proxmox Virtual Environment API", DefaultFunc: schema.MultiEnvDefaultFunc( []string{"PROXMOX_VE_ENDPOINT", "PM_VE_ENDPOINT"}, - "", + dvProviderVirtualEnvironmentEndpoint, ), ValidateFunc: func(v interface{}, k string) (warns []string, errs []error) { value := v.(string) @@ -106,13 +112,22 @@ func Provider() *schema.Provider { return false, nil }, }, + mkProviderVirtualEnvironmentOTP: { + Type: schema.TypeString, + Optional: true, + Description: "The one-time password for the Proxmox Virtual Environment API", + DefaultFunc: schema.MultiEnvDefaultFunc( + []string{"PROXMOX_VE_OTP", "PM_VE_OTP"}, + dvProviderVirtualEnvironmentOTP, + ), + }, mkProviderVirtualEnvironmentPassword: { Type: schema.TypeString, Optional: true, Description: "The password for the Proxmox Virtual Environment API", DefaultFunc: schema.MultiEnvDefaultFunc( []string{"PROXMOX_VE_PASSWORD", "PM_VE_PASSWORD"}, - "", + dvProviderVirtualEnvironmentPassword, ), ValidateFunc: func(v interface{}, k string) (warns []string, errs []error) { value := v.(string) @@ -132,7 +147,7 @@ func Provider() *schema.Provider { Description: "The username for the Proxmox Virtual Environment API", DefaultFunc: schema.MultiEnvDefaultFunc( []string{"PROXMOX_VE_USERNAME", "PM_VE_USERNAME"}, - "", + dvProviderVirtualEnvironmentUsername, ), ValidateFunc: func(v interface{}, k string) (warns []string, errs []error) { value := v.(string) @@ -168,6 +183,7 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { veConfig[mkProviderVirtualEnvironmentEndpoint].(string), veConfig[mkProviderVirtualEnvironmentUsername].(string), veConfig[mkProviderVirtualEnvironmentPassword].(string), + veConfig[mkProviderVirtualEnvironmentOTP].(string), veConfig[mkProviderVirtualEnvironmentInsecure].(bool), ) diff --git a/proxmoxtf/provider_test.go b/proxmoxtf/provider_test.go index a293726e..ce5f2432 100644 --- a/proxmoxtf/provider_test.go +++ b/proxmoxtf/provider_test.go @@ -38,6 +38,7 @@ func TestProviderSchema(t *testing.T) { testOptionalArguments(t, veSchema, []string{ mkProviderVirtualEnvironmentEndpoint, mkProviderVirtualEnvironmentInsecure, + mkProviderVirtualEnvironmentOTP, mkProviderVirtualEnvironmentPassword, mkProviderVirtualEnvironmentUsername, }) @@ -45,6 +46,7 @@ func TestProviderSchema(t *testing.T) { testValueTypes(t, veSchema, map[string]schema.ValueType{ mkProviderVirtualEnvironmentEndpoint: schema.TypeString, mkProviderVirtualEnvironmentInsecure: schema.TypeBool, + mkProviderVirtualEnvironmentOTP: schema.TypeString, mkProviderVirtualEnvironmentPassword: schema.TypeString, mkProviderVirtualEnvironmentUsername: schema.TypeString, })