From 6df1bd3630f3e75e506803f2a4a4c7a4e315ff0f Mon Sep 17 00:00:00 2001 From: David Bond Date: Mon, 26 Sep 2022 21:55:35 +0100 Subject: [PATCH] Add method for ACL validation This commit adds a `ValidateACL` method to the `Client` type that will be used to check the ACL is well formatted and the specified tests pass. It uses the JSON object method described in the API documentation. This will be required for implementing additional validation as requested on the terraform provider: https://github.com/tailscale/terraform-provider-tailscale/issues/158 Signed-off-by: David Bond --- tailscale/client.go | 12 ++++++++++ tailscale/client_test.go | 49 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/tailscale/client.go b/tailscale/client.go index ac82717..a02c62c 100644 --- a/tailscale/client.go +++ b/tailscale/client.go @@ -322,6 +322,18 @@ func (c *Client) SetACL(ctx context.Context, acl ACL) error { return c.performRequest(req, nil) } +// ValidateACL validates the provided ACL via the API. +func (c *Client) ValidateACL(ctx context.Context, acl ACL) error { + const uriFmt = "/api/v2/tailnet/%s/acl/validate" + + req, err := c.buildRequest(ctx, http.MethodPost, fmt.Sprintf(uriFmt, c.tailnet), acl) + if err != nil { + return err + } + + return c.performRequest(req, nil) +} + type DNSPreferences struct { MagicDNS bool `json:"magicDNS"` } diff --git a/tailscale/client_test.go b/tailscale/client_test.go index 4478931..df25003 100644 --- a/tailscale/client_test.go +++ b/tailscale/client_test.go @@ -756,3 +756,52 @@ func TestErrorData(t *testing.T) { assert.Empty(t, tailscale.ErrorData(io.EOF)) }) } + +func TestClient_ValidateACL(t *testing.T) { + t.Parallel() + + client, server := NewTestHarness(t) + + acl := tailscale.ACL{ + ACLs: []tailscale.ACLEntry{ + { + Action: "accept", + Ports: []string{"*:*"}, + Users: []string{"*"}, + }, + }, + TagOwners: map[string][]string{ + "tag:example": {"group:example"}, + }, + Hosts: map[string]string{ + "example-host-1": "10.0.0.0/8", + "example-host-2": "10.0.0.1", + }, + Groups: map[string][]string{ + "group:example": { + "user1@example.com", + "user2@example.com", + }, + }, + Tests: []tailscale.ACLTest{ + { + User: "user1@example.com", + Allow: []string{"example-host-1:22", "example-host-2:80"}, + Deny: []string{"exapmle-host-2:100"}, + }, + { + User: "user2@example.com", + Allow: []string{"100.64.0.1:22"}, + }, + }, + } + + server.ResponseCode = http.StatusOK + server.ResponseBody = acl + + err := client.ValidateACL(context.Background(), acl) + assert.NoError(t, err) + assert.EqualValues(t, server.ResponseBody, acl) + assert.EqualValues(t, http.MethodPost, server.Method) + assert.EqualValues(t, "/api/v2/tailnet/example.com/acl/validate", server.Path) +}