provider: add bearer token auth
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ terraform {
|
||||
|
||||
provider "artifactapi" {
|
||||
endpoint = "https://artifactapi.k8s.syd1.au.unkin.net"
|
||||
# token is read from ARTIFACTAPI_TOKEN when unset
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user