diff --git a/policyfile.go b/policyfile.go index 76d310a..d848ba8 100644 --- a/policyfile.go +++ b/policyfile.go @@ -74,6 +74,9 @@ type ACL struct { Postures map[string][]string `json:"postures,omitempty" hujson:"Postures,omitempty"` DefaultSourcePosture []string `json:"defaultSrcPosture,omitempty" hujson:"DefaultSrcPosture,omitempty"` + // AttrConfig maps attribute names to their configuration for custom device attributes. + AttrConfig map[string]ACLAttrConfig `json:"attrConfig,omitempty" hujson:"AttrConfig,omitempty"` + // ETag is the etag corresponding to this version of the ACL ETag string `json:"-"` } @@ -159,6 +162,16 @@ type NodeAttrGrantApp struct { Domains []string `json:"domains,omitempty" hujson:"Domains,omitempty"` } +// ACLAttrConfig represents configuration for a custom device attribute. +type ACLAttrConfig struct { + // Type can be one of "string", "bool", or "number". + Type string `json:"type,omitempty" hujson:"Type,omitempty"` + // AllowSetByNode indicates if nodes can set this attribute via LocalAPI. + AllowSetByNode bool `json:"allowSetByNode,omitempty" hujson:"AllowSetByNode,omitempty"` + // BroadcastToPeers is a list of destinations which should receive this attribute value, e.g. ["tag:admin"]. + BroadcastToPeers []string `json:"broadcastToPeers,omitempty" hujson:"BroadcastToPeers,omitempty"` +} + // Get retrieves the [ACL] that is currently set for the tailnet. func (pr *PolicyFileResource) Get(ctx context.Context) (*ACL, error) { req, err := pr.buildRequest(ctx, http.MethodGet, pr.buildTailnetURL("acl")) diff --git a/policyfile_test.go b/policyfile_test.go index 53c3703..a4a7f89 100644 --- a/policyfile_test.go +++ b/policyfile_test.go @@ -175,6 +175,22 @@ func TestACL_Unmarshal(t *testing.T) { "tag:monitoring": {"group:devops"}, "tag:prod": {"group:devops"}, }, + AttrConfig: map[string]ACLAttrConfig{ + "custom:example": { + Type: "string", + AllowSetByNode: true, + BroadcastToPeers: []string{"*"}, + }, + "custom:secure": { + Type: "bool", + AllowSetByNode: false, + BroadcastToPeers: []string{"tag:admin"}, + }, + "custom:priority": { + Type: "number", + AllowSetByNode: true, + }, + }, DERPMap: (*ACLDERPMap)(nil), SSH: []ACLSSH{ { diff --git a/testdata/acl.hujson b/testdata/acl.hujson index 7074104..c556380 100644 --- a/testdata/acl.hujson +++ b/testdata/acl.hujson @@ -24,6 +24,26 @@ // users in group:devops can apply the tag tag:prod "tag:prod": ["group:devops"], }, + "attrConfig": { + // example string attribute that nodes can set + "custom:example": { + "type": "string", + "allowSetByNode": true, + "broadcastToPeers": ["*"] + }, + // secure boolean attribute only settable by admin + "custom:secure": { + "type": "bool", + "allowSetByNode": false, + "broadcastToPeers": ["tag:admin"] + }, + // priority number attribute nodes can set themselves + "custom:priority": { + "type": "number", + "allowSetByNode": true, + // no broadcastToPeers means it won't be broadcast + } + }, "tests": [ { "src": "carl@example.com",