0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-06-30 02:31:10 +00:00

feat(access): add proxmox user token (#1159)

This commit is contained in:
Serge 2024-05-08 22:26:33 +03:00 committed by GitHub
parent 41ec34e04b
commit 8220271eee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 670 additions and 21 deletions

View File

@ -0,0 +1,61 @@
---
layout: page
title: proxmox_virtual_environment_user_token
parent: Resources
subcategory: Virtual Environment
description: |-
User API tokens.
---
# Resource: proxmox_virtual_environment_user_token
User API tokens.
## Example Usage
```terraform
# if creating a user token, the user must be created first
resource "proxmox_virtual_environment_user" "user" {
comment = "Managed by Terraform"
email = "user@pve"
enabled = true
expiration_date = "2034-01-01T22:00:00Z"
user_id = "user@pve"
}
resource "proxmox_virtual_environment_user_token" "user_token" {
comment = "Managed by Terraform"
expiration_date = "2033-01-01T22:00:00Z"
token_name = "tk1"
user_id = proxmox_virtual_environment_user.user.user_id
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `token_name` (String) User-specific token identifier.
- `user_id` (String) User identifier.
### Optional
- `comment` (String) Comment for the token.
- `expiration_date` (String) Expiration date for the token.
- `privileges_separation` (Boolean) Restrict API token privileges with separate ACLs (default), or give full privileges of corresponding user.
### Read-Only
- `id` (String) Unique token identifier with format `<user_id>!<token_name>`.
- `value` (String, Sensitive) API token value used for authentication. It is populated only when creating a new token, and can't be retrieved at import.
## Import
Import is supported using the following syntax:
```shell
#!/usr/bin/env sh
#Tokens can be imported using they identifiers in format `user_id!token_name` format, e.g.:
terraform import proxmox_virtual_environment_user_token.token1 user@pve!token1
```

View File

@ -0,0 +1,3 @@
#!/usr/bin/env sh
#Tokens can be imported using they identifiers in format `user_id!token_name` format, e.g.:
terraform import proxmox_virtual_environment_user_token.token1 user@pve!token1

View File

@ -0,0 +1,15 @@
# if creating a user token, the user must be created first
resource "proxmox_virtual_environment_user" "user" {
comment = "Managed by Terraform"
email = "user@pve"
enabled = true
expiration_date = "2034-01-01T22:00:00Z"
user_id = "user@pve"
}
resource "proxmox_virtual_environment_user_token" "user_token" {
comment = "Managed by Terraform"
expiration_date = "2033-01-01T22:00:00Z"
token_name = "tk1"
user_id = proxmox_virtual_environment_user.user.user_id
}

View File

@ -0,0 +1,306 @@
package access
import (
"context"
"fmt"
"regexp"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/bpg/terraform-provider-proxmox/fwprovider/structure"
"github.com/bpg/terraform-provider-proxmox/fwprovider/validators"
"github.com/bpg/terraform-provider-proxmox/proxmox"
"github.com/bpg/terraform-provider-proxmox/proxmox/access"
proxmoxtypes "github.com/bpg/terraform-provider-proxmox/proxmox/types"
)
var (
_ resource.Resource = &userTokenResource{}
_ resource.ResourceWithConfigure = &userTokenResource{}
_ resource.ResourceWithImportState = &userTokenResource{}
)
type userTokenResource struct {
client proxmox.Client
}
type userTokenModel struct {
Comment types.String `tfsdk:"comment"`
ExpirationDate types.String `tfsdk:"expiration_date"`
ID types.String `tfsdk:"id"`
PrivSeparation types.Bool `tfsdk:"privileges_separation"`
UserID types.String `tfsdk:"user_id"`
TokenName types.String `tfsdk:"token_name"`
Value types.String `tfsdk:"value"`
}
// NewUserTokenResource creates a new user token resource.
func NewUserTokenResource() resource.Resource {
return &userTokenResource{}
}
func (r *userTokenResource) Schema(
_ context.Context,
_ resource.SchemaRequest,
resp *resource.SchemaResponse,
) {
resp.Schema = schema.Schema{
Description: "User API tokens.",
Attributes: map[string]schema.Attribute{
"comment": schema.StringAttribute{
Description: "Comment for the token.",
Optional: true,
},
"expiration_date": schema.StringAttribute{
Description: "Expiration date for the token.",
Optional: true,
Validators: []validator.String{
validators.NewParseValidator(func(s string) (time.Time, error) {
return time.Parse(time.RFC3339, s)
}, "must be a valid RFC3339 date"),
},
},
"id": structure.IDAttribute("Unique token identifier with format `<user_id>!<token_name>`."),
"privileges_separation": schema.BoolAttribute{
Description: "Restrict API token privileges with separate ACLs (default)",
MarkdownDescription: "Restrict API token privileges with separate ACLs (default), " +
"or give full privileges of corresponding user.",
Optional: true,
Computed: true,
Default: booldefault.StaticBool(true),
},
"token_name": schema.StringAttribute{
Description: "User-specific token identifier.",
Required: true,
Validators: []validator.String{
stringvalidator.RegexMatches(regexp.MustCompile(`[A-Za-z][A-Za-z0-9.\-_]+`), "must be a valid token identifier"),
},
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"user_id": schema.StringAttribute{
Description: "User identifier.",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"value": schema.StringAttribute{
Description: "API token value used for authentication.",
MarkdownDescription: "API token value used for authentication. It is populated only when creating a new token, " +
"and can't be retrieved at import.",
Computed: true,
Sensitive: true,
},
},
}
}
func (r *userTokenResource) Configure(
_ context.Context,
req resource.ConfigureRequest,
resp *resource.ConfigureResponse,
) {
if req.ProviderData == nil {
return
}
client, ok := req.ProviderData.(proxmox.Client)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Resource Configure Type",
fmt.Sprintf("Expected *proxmox.Client, got: %T", req.ProviderData),
)
return
}
r.client = client
}
func (r *userTokenResource) Metadata(
_ context.Context,
req resource.MetadataRequest,
resp *resource.MetadataResponse,
) {
resp.TypeName = req.ProviderTypeName + "_user_token"
}
func (r *userTokenResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var plan userTokenModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
body := access.UserTokenCreateRequestBody{
Comment: plan.Comment.ValueStringPointer(),
PrivSeparate: proxmoxtypes.CustomBoolPtr(plan.PrivSeparation.ValueBoolPointer()),
}
if !plan.ExpirationDate.IsNull() && plan.ExpirationDate.ValueString() != "" {
expirationDate, err := time.Parse(
time.RFC3339,
plan.ExpirationDate.ValueString(),
)
if err != nil {
resp.Diagnostics.AddError("Error parsing expiration date", err.Error())
return
}
v := expirationDate.Unix()
body.ExpirationDate = &v
}
value, err := r.client.Access().CreateUserToken(ctx, plan.UserID.ValueString(), plan.TokenName.ValueString(), &body)
if err != nil {
resp.Diagnostics.AddError("Error creating user token", err.Error())
}
if resp.Diagnostics.HasError() {
return
}
plan.ID = types.StringValue(plan.UserID.ValueString() + "!" + plan.TokenName.ValueString())
plan.Value = types.StringValue(value)
resp.State.Set(ctx, plan)
}
func (r *userTokenResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var state userTokenModel
diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
data, err := r.client.Access().GetUserToken(ctx, state.UserID.ValueString(), state.TokenName.ValueString())
if err != nil {
resp.Diagnostics.AddError("Error reading user token", err.Error())
return
}
state.Comment = types.StringPointerValue(data.Comment)
if data.ExpirationDate != nil {
dt := time.Unix(int64(*data.ExpirationDate), 0).UTC().Format(time.RFC3339)
state.ExpirationDate = types.StringValue(dt)
}
state.PrivSeparation = types.BoolPointerValue(data.PrivSeparate.PointerBool())
diags = resp.State.Set(ctx, state)
resp.Diagnostics.Append(diags...)
}
func (r *userTokenResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var plan, state userTokenModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
body := access.UserTokenUpdateRequestBody{
Comment: plan.Comment.ValueStringPointer(),
PrivSeparate: proxmoxtypes.CustomBoolPtr(plan.PrivSeparation.ValueBoolPointer()),
}
if !plan.ExpirationDate.IsNull() && plan.ExpirationDate.ValueString() != "" {
expirationDate, err := time.Parse(
time.RFC3339,
plan.ExpirationDate.ValueString(),
)
if err != nil {
resp.Diagnostics.AddError("Error parsing expiration date", err.Error())
return
}
v := expirationDate.Unix()
body.ExpirationDate = &v
}
err := r.client.Access().UpdateUserToken(ctx, plan.UserID.ValueString(), plan.TokenName.ValueString(), &body)
if err != nil {
resp.Diagnostics.AddError("Error creating user token", err.Error())
}
if resp.Diagnostics.HasError() {
return
}
plan.Value = types.StringNull()
resp.State.Set(ctx, plan)
}
func (r *userTokenResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var state userTokenModel
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
err := r.client.Access().DeleteUserToken(ctx, state.UserID.ValueString(), state.TokenName.ValueString())
if err != nil {
resp.Diagnostics.AddError("Error deleting user token", err.Error())
return
}
resp.State.RemoveResource(ctx)
}
func (r *userTokenResource) ImportState(
ctx context.Context,
req resource.ImportStateRequest,
resp *resource.ImportStateResponse,
) {
idParts := strings.Split(req.ID, "!")
if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
resp.Diagnostics.AddError(
"Unexpected Import Identifier",
fmt.Sprintf("Expected import identifier with format: 'user_id!token_name'. Got: %q", req.ID),
)
return
}
userID := idParts[0]
tokenName := idParts[1]
data, err := r.client.Access().GetUserToken(ctx, userID, tokenName)
if err != nil {
resp.Diagnostics.AddError("Error reading user token", err.Error())
return
}
state := userTokenModel{
Comment: types.StringPointerValue(data.Comment),
ID: types.StringValue(req.ID),
PrivSeparation: types.BoolPointerValue(data.PrivSeparate.PointerBool()),
UserID: types.StringValue(userID),
TokenName: types.StringValue(tokenName),
Value: types.StringNull(),
}
if data.ExpirationDate != nil {
state.ExpirationDate = types.StringValue(time.Unix(int64(*data.ExpirationDate), 0).UTC().Format(time.RFC3339))
}
diags := resp.State.Set(ctx, state)
resp.Diagnostics.Append(diags...)
}

View File

@ -25,6 +25,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/bpg/terraform-provider-proxmox/fwprovider/access"
"github.com/bpg/terraform-provider-proxmox/fwprovider/ha" "github.com/bpg/terraform-provider-proxmox/fwprovider/ha"
"github.com/bpg/terraform-provider-proxmox/fwprovider/hardwaremapping" "github.com/bpg/terraform-provider-proxmox/fwprovider/hardwaremapping"
"github.com/bpg/terraform-provider-proxmox/fwprovider/network" "github.com/bpg/terraform-provider-proxmox/fwprovider/network"
@ -446,6 +447,7 @@ func (p *proxmoxProvider) Resources(_ context.Context) []func() resource.Resourc
hardwaremapping.NewResourceUSB, hardwaremapping.NewResourceUSB,
network.NewLinuxBridgeResource, network.NewLinuxBridgeResource,
network.NewLinuxVLANResource, network.NewLinuxVLANResource,
access.NewUserTokenResource,
vm.NewVMResource, vm.NewVMResource,
NewClusterOptionsResource, NewClusterOptionsResource,
NewDownloadFileResource, NewDownloadFileResource,

View File

@ -7,9 +7,15 @@
package tests package tests
import ( import (
"context"
"fmt"
"testing" "testing"
"github.com/brianvoe/gofakeit/v7"
"github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/stretchr/testify/require"
"github.com/bpg/terraform-provider-proxmox/proxmox/access"
) )
func TestAccResourceUser(t *testing.T) { func TestAccResourceUser(t *testing.T) {
@ -17,45 +23,55 @@ func TestAccResourceUser(t *testing.T) {
te := initTestEnvironment(t) te := initTestEnvironment(t)
username := fmt.Sprintf("%s@pve", gofakeit.Username())
te.addTemplateVars(map[string]any{
"Username": username,
})
tests := []struct { tests := []struct {
name string name string
steps []resource.TestStep steps []resource.TestStep
}{ }{
{"create and update user", []resource.TestStep{ {"create and update user", []resource.TestStep{
{ {
Config: `resource "proxmox_virtual_environment_user" "user1" { Config: te.renderConfig(`resource "proxmox_virtual_environment_user" "user" {
comment = "Managed by Terraform" comment = "Managed by Terraform"
email = "user1@pve" email = "{{.Username}}"
enabled = true enabled = true
expiration_date = "2034-01-01T22:00:00Z" expiration_date = "2034-01-01T22:00:00Z"
first_name = "First" first_name = "First"
last_name = "Last" last_name = "Last"
user_id = "user1@pve" user_id = "{{.Username}}"
}`, }`),
Check: testResourceAttributes("proxmox_virtual_environment_user.user1", map[string]string{ Check: testResourceAttributes("proxmox_virtual_environment_user.user", map[string]string{
"comment": "Managed by Terraform", "comment": "Managed by Terraform",
"email": "user1@pve", "email": username,
"enabled": "true", "enabled": "true",
"expiration_date": "2034-01-01T22:00:00Z", "expiration_date": "2034-01-01T22:00:00Z",
"first_name": "First", "first_name": "First",
"last_name": "Last", "last_name": "Last",
"user_id": "user1@pve", "user_id": username,
}), }),
}, },
{ {
Config: `resource "proxmox_virtual_environment_user" "user1" { Config: te.renderConfig(`resource "proxmox_virtual_environment_user" "user" {
enabled = false enabled = false
expiration_date = "2035-01-01T22:00:00Z" expiration_date = "2035-01-01T22:00:00Z"
user_id = "user1@pve" user_id = "{{.Username}}"
first_name = "First One" first_name = "First One"
}`, }`),
Check: testResourceAttributes("proxmox_virtual_environment_user.user1", map[string]string{ Check: testResourceAttributes("proxmox_virtual_environment_user.user", map[string]string{
"enabled": "false", "enabled": "false",
"expiration_date": "2035-01-01T22:00:00Z", "expiration_date": "2035-01-01T22:00:00Z",
"first_name": "First One", "first_name": "First One",
"user_id": "user1@pve", "user_id": username,
}), }),
}, },
{
ResourceName: "proxmox_virtual_environment_user.user",
ImportState: true,
ImportStateVerify: true,
},
}}, }},
} }
@ -68,3 +84,91 @@ func TestAccResourceUser(t *testing.T) {
}) })
} }
} }
func TestAccResourceUserToken(t *testing.T) {
t.Parallel()
te := initTestEnvironment(t)
username := fmt.Sprintf("%s@pve", gofakeit.Username())
tokenName := gofakeit.Word()
te.addTemplateVars(map[string]any{
"Username": username,
"TokenName": tokenName,
})
tests := []struct {
name string
preCheck func()
steps []resource.TestStep
}{
{
"create and update user token",
func() {
err := te.accessClient().CreateUser(context.Background(), &access.UserCreateRequestBody{
ID: username,
Password: gofakeit.Password(true, true, true, true, false, 8),
})
require.NoError(t, err)
t.Cleanup(func() {
err := te.accessClient().DeleteUser(context.Background(), username)
require.NoError(t, err)
})
},
[]resource.TestStep{
{
Config: te.renderConfig(`resource "proxmox_virtual_environment_user_token" "user_token" {
comment = "Managed by Terraform"
expiration_date = "2034-01-01T22:00:00Z"
token_name = "{{.TokenName}}"
user_id = "{{.Username}}"
}`),
Check: testResourceAttributes("proxmox_virtual_environment_user_token.user_token", map[string]string{
"comment": "Managed by Terraform",
"expiration_date": "2034-01-01T22:00:00Z",
"id": fmt.Sprintf("%s!%s", username, tokenName),
"user_id": username,
"value": fmt.Sprintf("%s!%s=.*", username, tokenName),
}),
},
{
Config: te.renderConfig(`resource "proxmox_virtual_environment_user_token" "user_token" {
comment = "Managed by Terraform 2"
expiration_date = "2033-01-01T01:01:01Z"
privileges_separation = false
token_name = "{{.TokenName}}"
user_id = "{{.Username}}"
}`),
Check: resource.ComposeTestCheckFunc(
testResourceAttributes("proxmox_virtual_environment_user_token.user_token", map[string]string{
"comment": "Managed by Terraform 2",
"expiration_date": "2033-01-01T01:01:01Z",
"privileges_separation": "false",
"token_name": tokenName,
"user_id": username,
}),
testNoResourceAttributesSet("proxmox_virtual_environment_user_token.user_token", []string{
"value",
}),
),
},
{
ResourceName: "proxmox_virtual_environment_user_token.user_token",
ImportState: true,
ImportStateVerify: true,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: te.accProviders,
PreCheck: tt.preCheck,
Steps: tt.steps,
})
})
}
}

View File

@ -17,6 +17,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/bpg/terraform-provider-proxmox/proxmox/access"
sdkV2provider "github.com/bpg/terraform-provider-proxmox/proxmoxtf/provider" sdkV2provider "github.com/bpg/terraform-provider-proxmox/proxmoxtf/provider"
"github.com/bpg/terraform-provider-proxmox/fwprovider" "github.com/bpg/terraform-provider-proxmox/fwprovider"
@ -35,7 +36,7 @@ type testEnvironment struct {
accProviders map[string]func() (tfprotov6.ProviderServer, error) accProviders map[string]func() (tfprotov6.ProviderServer, error)
once sync.Once once sync.Once
nc *nodes.Client c api.Client
} }
func initTestEnvironment(t *testing.T) *testEnvironment { func initTestEnvironment(t *testing.T) *testEnvironment {
@ -109,8 +110,8 @@ func (e *testEnvironment) renderConfig(cfg string) string {
return buf.String() return buf.String()
} }
func (e *testEnvironment) nodeClient() *nodes.Client { func (e *testEnvironment) client() api.Client {
if e.nc == nil { if e.c == nil {
e.once.Do( e.once.Do(
func() { func() {
username := utils.GetAnyStringEnv("PROXMOX_VE_USERNAME") username := utils.GetAnyStringEnv("PROXMOX_VE_USERNAME")
@ -128,21 +129,26 @@ func (e *testEnvironment) nodeClient() *nodes.Client {
panic(err) panic(err)
} }
client, err := api.NewClient(creds, conn) e.c, err = api.NewClient(creds, conn)
if err != nil { if err != nil {
panic(err) panic(err)
} }
e.nc = &nodes.Client{Client: client, NodeName: e.nodeName}
}) })
} }
return e.nc return e.c
}
func (e *testEnvironment) accessClient() *access.Client {
return &access.Client{Client: e.client()}
}
func (e *testEnvironment) nodeClient() *nodes.Client {
return &nodes.Client{Client: e.client(), NodeName: e.nodeName}
} }
func (e *testEnvironment) nodeStorageClient() *storage.Client { func (e *testEnvironment) nodeStorageClient() *storage.Client {
nodesClient := e.nodeClient() return &storage.Client{Client: e.nodeClient(), StorageName: e.datastoreID}
return &storage.Client{Client: nodesClient, StorageName: e.datastoreID}
} }
// testAccMuxProviders returns a map of mux servers for the acceptance tests. // testAccMuxProviders returns a map of mux servers for the acceptance tests.

View File

@ -0,0 +1,93 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package access
import (
"context"
"fmt"
"net/http"
"net/url"
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
)
func (c *Client) userTokensPath(id string) string {
return fmt.Sprintf("%s/%s/token", c.usersPath(), url.PathEscape(id))
}
func (c *Client) userTokenPath(userid, id string) string {
return fmt.Sprintf("%s/%s", c.userTokensPath(userid), url.PathEscape(id))
}
// CreateUserToken creates a user token.
func (c *Client) CreateUserToken(
ctx context.Context,
userid string,
id string,
d *UserTokenCreateRequestBody,
) (string, error) {
resBody := &UserTokenCreateResponseBody{}
err := c.DoRequest(ctx, http.MethodPost, c.userTokenPath(userid, id), d, resBody)
if err != nil {
return "", fmt.Errorf("error creating user token: %w", err)
}
return resBody.Data.FullTokenID + "=" + resBody.Data.Value, nil
}
// DeleteUserToken deletes an user token.
func (c *Client) DeleteUserToken(ctx context.Context, userid string, id string) error {
err := c.DoRequest(ctx, http.MethodDelete, c.userTokenPath(userid, id), nil, nil)
if err != nil {
return fmt.Errorf("error deleting user token: %w", err)
}
return nil
}
// GetUserToken retrieves a user token.
func (c *Client) GetUserToken(ctx context.Context, userid string, id string) (*UserTokenGetResponseData, error) {
resBody := &UserTokenGetResponseBody{}
err := c.DoRequest(ctx, http.MethodGet, c.userTokenPath(userid, id), nil, resBody)
if err != nil {
return nil, fmt.Errorf("error retrieving user token: %w", err)
}
if resBody.Data == nil {
return nil, api.ErrNoDataObjectInResponse
}
return resBody.Data, nil
}
// ListUserTokens retrieves a list of user tokens.
func (c *Client) ListUserTokens(ctx context.Context, userid string) ([]*UserTokenListResponseData, error) {
resBody := &UserTokenListResponseBody{}
err := c.DoRequest(ctx, http.MethodGet, c.userTokensPath(userid), nil, resBody)
if err != nil {
return nil, fmt.Errorf("error listing user tokens: %w", err)
}
if resBody.Data == nil {
return nil, api.ErrNoDataObjectInResponse
}
return resBody.Data, nil
}
// UpdateUserToken updates the user token.
func (c *Client) UpdateUserToken(ctx context.Context, userid string, id string, d *UserTokenUpdateRequestBody) error {
err := c.DoRequest(ctx, http.MethodPut, c.userTokenPath(userid, id), d, nil)
if err != nil {
return fmt.Errorf("error updating user token: %w", err)
}
return nil
}

View File

@ -0,0 +1,58 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package access
import (
"github.com/bpg/terraform-provider-proxmox/proxmox/types"
)
// UserTokenCreateRequestBody contains the data for a user token create request.
type UserTokenCreateRequestBody struct {
Comment *string `json:"comment,omitempty" url:"comment,omitempty"`
ExpirationDate *int64 `json:"expire,omitempty" url:"expire,omitempty"`
PrivSeparate *types.CustomBool `json:"privsep,omitempty" url:"privsep,omitempty,int"`
}
// UserTokenUpdateRequestBody contains the data for a user token update request.
type UserTokenUpdateRequestBody UserTokenCreateRequestBody
// UserTokenCreateResponseBody contains the body from a user token create response.
type UserTokenCreateResponseBody struct {
Data *UserTokenCreateResponseData `json:"data,omitempty"`
}
// UserTokenCreateResponseData contains the data from a user token create response.
type UserTokenCreateResponseData struct {
// The full token id, format "<userid>!<tokenid>"
FullTokenID string `json:"full-tokenid"`
Info UserTokenGetResponseData `json:"info"`
Value string `json:"value"`
}
// UserTokenGetResponseBody contains the body from a user token get response.
type UserTokenGetResponseBody struct {
Data *UserTokenGetResponseData `json:"data,omitempty"`
}
// UserTokenGetResponseData contains the data from a user token get response.
type UserTokenGetResponseData struct {
Comment *string `json:"comment,omitempty" url:"comment,omitempty"`
PrivSeparate *types.CustomBool `json:"privsep,omitempty" url:"privsep,omitempty,int"`
ExpirationDate *types.CustomInt64 `json:"expire,omitempty" url:"expire,omitempty"`
}
// UserTokenListResponseBody contains the body from a user token list response.
type UserTokenListResponseBody struct {
Data []*UserTokenListResponseData `json:"data,omitempty"`
}
// UserTokenListResponseData contains the data from a user token list response.
type UserTokenListResponseData struct {
UserTokenGetResponseData
TokenID string `json:"tokenid"`
}

View File

@ -45,4 +45,5 @@ import (
//go:generate cp ../build/docs-gen/resources/virtual_environment_haresource.md ../docs/resources/ //go:generate cp ../build/docs-gen/resources/virtual_environment_haresource.md ../docs/resources/
//go:generate cp ../build/docs-gen/resources/virtual_environment_cluster_options.md ../docs/resources/ //go:generate cp ../build/docs-gen/resources/virtual_environment_cluster_options.md ../docs/resources/
//go:generate cp ../build/docs-gen/resources/virtual_environment_download_file.md ../docs/resources/ //go:generate cp ../build/docs-gen/resources/virtual_environment_download_file.md ../docs/resources/
//go:generate cp ../build/docs-gen/resources/virtual_environment_user_token.md ../docs/resources/
//go:generate cp ../build/docs-gen/resources/virtual_environment_vm2.md ../docs/resources/ //go:generate cp ../build/docs-gen/resources/virtual_environment_vm2.md ../docs/resources/