Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions docs/resources/org_access_token.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "docker_org_access_token Resource - docker"
subcategory: ""
description: |-
Manages organization access tokens.
-> Note: This resource is only available when authenticated with a username and password as an owner of the org.
Example Usage

resource "docker_org_access_token" "example" {
org_name = "my-organization"
label = "ci-token"
description = "Token for CI pulls"
resources = [
{
type = "TYPE_REPO"
path = "my-organization/my-repository"
scopes = ["repo-pull"]
}
]
expires_at = "2027-12-31T23:59:59Z"
}

Public-Only Repositories
Use the special path */*/public to scope the token to public repositories only.

resource "docker_org_access_token" "public_pull" {
org_name = "my-organization"
label = "public-pull-token"

resources = [
{
type = "TYPE_REPO"
path = "*/*/public"
scopes = ["repo-pull"]
}
]
}
---

# docker_org_access_token (Resource)

Manages organization access tokens.

-> **Note**: This resource is only available when authenticated with a username and password as an owner of the org.

## Example Usage

```hcl
resource "docker_org_access_token" "example" {
org_name = "my-organization"
label = "ci-token"
description = "Token for CI pulls"
resources = [
{
type = "TYPE_REPO"
path = "my-organization/my-repository"
scopes = ["repo-pull"]
}
]
expires_at = "2027-12-31T23:59:59Z"
}
```

## Public-Only Repositories

Use the special path `*/*/public` to scope the token to public repositories only.

```hcl
resource "docker_org_access_token" "public_pull" {
org_name = "my-organization"
label = "public-pull-token"

resources = [
{
type = "TYPE_REPO"
path = "*/*/public"
scopes = ["repo-pull"]
}
]
}
```



<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `label` (String) Label for the access token
- `org_name` (String) The organization namespace
- `resources` (Attributes List) Resources this token has access to (see [below for nested schema](#nestedatt--resources))

### Optional

- `description` (String) Description for the access token
- `expires_at` (String) Expiration date for the token. Changing this value recreates the token.

### Read-Only

- `created_at` (String) The creation time of the access token
- `created_by` (String) The user that created the access token
- `id` (String) The ID of the organization access token
- `token` (String, Sensitive) The organization access token. This value is only returned during creation.

<a id="nestedatt--resources"></a>
### Nested Schema for `resources`

Required:

- `path` (String) The path of the resource
- `scopes` (List of String) The scopes this token has access to
- `type` (String) The type of resource
118 changes: 118 additions & 0 deletions internal/hubclient/client_org_access_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
Copyright 2024 Docker Terraform Provider authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package hubclient

import (
"bytes"
"context"
"encoding/json"
"fmt"
)

const (
OrgAccessTokenTypeRepo = "TYPE_REPO"
OrgAccessTokenTypeOrg = "TYPE_ORG"
)

type OrgAccessToken struct {
ID string `json:"id"`
Label string `json:"label"`
Description string `json:"description,omitempty"`
CreatedBy string `json:"created_by"`
IsActive bool `json:"is_active"`
CreatedAt string `json:"created_at"`
ExpiresAt string `json:"expires_at,omitempty"`
LastUsedAt string `json:"last_used_at,omitempty"`
Token string `json:"token,omitempty"`
Resources []OrgAccessTokenResource `json:"resources,omitempty"`
}

type OrgAccessTokenResource struct {
Type string `json:"type"`
Path string `json:"path"`
Scopes []string `json:"scopes"`
}

type OrgAccessTokenCreateParams struct {
Label string `json:"label"`
Description string `json:"description"`
Resources []OrgAccessTokenResource `json:"resources"`
ExpiresAt string `json:"expires_at,omitempty"`
}

type OrgAccessTokenUpdateParams struct {
Label string `json:"label"`
Description string `json:"description"`
Resources []OrgAccessTokenResource `json:"resources"`
}

func (c *Client) CreateOrgAccessToken(ctx context.Context, orgName string, params OrgAccessTokenCreateParams) (OrgAccessToken, error) {
if orgName == "" {
return OrgAccessToken{}, fmt.Errorf("orgName is required")
}

buf := bytes.NewBuffer(nil)
if err := json.NewEncoder(buf).Encode(params); err != nil {
return OrgAccessToken{}, err
}

var accessToken OrgAccessToken
err := c.sendRequest(ctx, "POST", fmt.Sprintf("/orgs/%s/access-tokens", orgName), buf.Bytes(), &accessToken)
return accessToken, err
}

func (c *Client) GetOrgAccessToken(ctx context.Context, orgName, accessTokenID string) (OrgAccessToken, error) {
if orgName == "" {
return OrgAccessToken{}, fmt.Errorf("orgName is required")
}
if accessTokenID == "" {
return OrgAccessToken{}, fmt.Errorf("accessTokenID is required")
}

var accessToken OrgAccessToken
err := c.sendRequest(ctx, "GET", fmt.Sprintf("/orgs/%s/access-tokens/%s", orgName, accessTokenID), nil, &accessToken)
return accessToken, err
}

func (c *Client) UpdateOrgAccessToken(ctx context.Context, orgName, accessTokenID string, params OrgAccessTokenUpdateParams) (OrgAccessToken, error) {
if orgName == "" {
return OrgAccessToken{}, fmt.Errorf("orgName is required")
}
if accessTokenID == "" {
return OrgAccessToken{}, fmt.Errorf("accessTokenID is required")
}

buf := bytes.NewBuffer(nil)
if err := json.NewEncoder(buf).Encode(params); err != nil {
return OrgAccessToken{}, err
}

var accessToken OrgAccessToken
err := c.sendRequest(ctx, "PATCH", fmt.Sprintf("/orgs/%s/access-tokens/%s", orgName, accessTokenID), buf.Bytes(), &accessToken)
return accessToken, err
}

func (c *Client) DeleteOrgAccessToken(ctx context.Context, orgName, accessTokenID string) error {
if orgName == "" {
return fmt.Errorf("orgName is required")
}
if accessTokenID == "" {
return fmt.Errorf("accessTokenID is required")
}

return c.sendRequest(ctx, "DELETE", fmt.Sprintf("/orgs/%s/access-tokens/%s", orgName, accessTokenID), nil, nil)
}
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ func getConfigfileKey(host string) string {
func (p *DockerProvider) Resources(ctx context.Context) []func() resource.Resource {
return []func() resource.Resource{
NewAccessTokenResource,
NewOrgAccessTokenResource,
NewOrgSettingImageAccessManagementResource,
NewOrgSettingRegistryAccessManagementResource,
NewOrgTeamResource,
Expand Down
Loading
Loading