provider: add bearer token auth
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful

This commit is contained in:
2026-10-14 22:20:15 +11:00
parent 70169163db
commit 517790abef
5 changed files with 108 additions and 3 deletions
+5 -3
View File
@@ -37,12 +37,14 @@ terraform {
provider "artifactapi" {
endpoint = "https://artifactapi.example.com"
token = var.artifactapi_token # optional
}
```
| Attribute | Required | Description |
|------------|----------|--------------------------------------|
| `endpoint` | Yes | ArtifactAPI server endpoint URL |
| Attribute | Required | Description |
|------------|----------|------------------------------------------------------------------------------|
| `endpoint` | Yes | ArtifactAPI server endpoint URL |
| `token` | No | Bearer token sent as `Authorization` header; falls back to `ARTIFACTAPI_TOKEN` |
## Resources
+1
View File
@@ -9,6 +9,7 @@ terraform {
provider "artifactapi" {
endpoint = "https://artifactapi.k8s.syd1.au.unkin.net"
# token is read from ARTIFACTAPI_TOKEN when unset
}
# ---------------------------------------------------------------------------
+4
View File
@@ -11,6 +11,7 @@ import (
type apiClient struct {
baseURL string
token string
httpClient *http.Client
}
@@ -54,6 +55,9 @@ func (c *apiClient) do(ctx context.Context, method, path string, body, out any)
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
if c.token != "" {
req.Header.Set("Authorization", "Bearer "+c.token)
}
resp, err := c.httpClient.Do(req)
if err != nil {
+11
View File
@@ -2,6 +2,7 @@ package provider
import (
"context"
"os"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/provider"
@@ -18,6 +19,7 @@ type ArtifactAPIProvider struct {
type artifactAPIProviderModel struct {
Endpoint types.String `tfsdk:"endpoint"`
Token types.String `tfsdk:"token"`
}
func New(version string) func() provider.Provider {
@@ -39,6 +41,11 @@ func (p *ArtifactAPIProvider) Schema(_ context.Context, _ provider.SchemaRequest
Description: "The ArtifactAPI server endpoint URL (e.g. https://artifactapi.example.com).",
Required: true,
},
"token": schema.StringAttribute{
Description: "Bearer token sent as the Authorization header. Falls back to the ARTIFACTAPI_TOKEN environment variable.",
Optional: true,
Sensitive: true,
},
},
}
}
@@ -51,6 +58,10 @@ func (p *ArtifactAPIProvider) Configure(ctx context.Context, req provider.Config
}
client := newAPIClient(config.Endpoint.ValueString())
client.token = config.Token.ValueString()
if client.token == "" {
client.token = os.Getenv("ARTIFACTAPI_TOKEN")
}
resp.DataSourceData = client
resp.ResourceData = client
}
+87
View File
@@ -0,0 +1,87 @@
package provider
import (
"context"
"net/http"
"net/http/httptest"
"testing"
fwprovider "github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)
func configuredClient(t *testing.T, endpoint string, token *string) *apiClient {
t.Helper()
p := &ArtifactAPIProvider{}
var schemaResp fwprovider.SchemaResponse
p.Schema(context.Background(), fwprovider.SchemaRequest{}, &schemaResp)
tokenVal := tftypes.NewValue(tftypes.String, nil)
if token != nil {
tokenVal = tftypes.NewValue(tftypes.String, *token)
}
raw := tftypes.NewValue(tftypes.Object{AttributeTypes: map[string]tftypes.Type{
"endpoint": tftypes.String,
"token": tftypes.String,
}}, map[string]tftypes.Value{
"endpoint": tftypes.NewValue(tftypes.String, endpoint),
"token": tokenVal,
})
var resp fwprovider.ConfigureResponse
p.Configure(context.Background(), fwprovider.ConfigureRequest{
Config: tfsdk.Config{Schema: schemaResp.Schema, Raw: raw},
}, &resp)
if resp.Diagnostics.HasError() {
t.Fatalf("configure: %v", resp.Diagnostics)
}
return resp.ResourceData.(*apiClient)
}
func TestProvider_BearerToken(t *testing.T) {
strp := func(s string) *string { return &s }
cases := []struct {
name string
attr *string
env string
want string
}{
{"attribute", strp("attr-tok"), "", "Bearer attr-tok"},
{"env fallback", nil, "env-tok", "Bearer env-tok"},
{"attribute wins over env", strp("attr-tok"), "env-tok", "Bearer attr-tok"},
{"unset", nil, "", ""},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
t.Setenv("ARTIFACTAPI_TOKEN", tc.env)
var got []string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
got = append(got, r.Header.Get("Authorization"))
_, present := r.Header["Authorization"]
if tc.want == "" && present {
t.Errorf("unexpected Authorization header on %s", r.Method)
}
w.WriteHeader(http.StatusNoContent)
}))
defer srv.Close()
c := configuredClient(t, srv.URL, tc.attr)
ctx := context.Background()
if err := c.get(ctx, "/x", nil); err != nil {
t.Fatal(err)
}
if err := c.put(ctx, "/x", map[string]string{"a": "b"}, nil); err != nil {
t.Fatal(err)
}
if err := c.del(ctx, "/x"); err != nil {
t.Fatal(err)
}
for _, h := range got {
if h != tc.want {
t.Errorf("Authorization = %q, want %q", h, tc.want)
}
}
})
}
}