From 0df272b42e1f383260af23c499ed935f94b5e463 Mon Sep 17 00:00:00 2001 From: Alejandro Bernal Date: Mon, 31 Mar 2025 10:32:32 -0500 Subject: [PATCH 1/3] add dynamic roles --- pkg/connector/role.go | 66 ++++++++++++++++++++++++++++++++++++------- pkg/jamf/client.go | 15 ++++++++++ pkg/jamf/models.go | 30 +++++++++++++------- 3 files changed, 91 insertions(+), 20 deletions(-) diff --git a/pkg/connector/role.go b/pkg/connector/role.go index ba32c4ea..744a2859 100644 --- a/pkg/connector/role.go +++ b/pkg/connector/role.go @@ -3,6 +3,7 @@ package connector import ( "context" "fmt" + "slices" "github.com/conductorone/baton-jamf/pkg/jamf" v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" @@ -22,11 +23,12 @@ func (o *roleResourceType) ResourceType(_ context.Context) *v2.ResourceType { return o.resourceType } -var privileges = []string{ +const CustomPrivilege = "Custom" + +var defaultPrivileges = []string{ "Administrator", "Auditor", "Enrollment Only", - "Custom", } // Create a new connector resource for a Jamf role. @@ -55,8 +57,22 @@ func roleResource(ctx context.Context, role string, parentResourceID *v2.Resourc } func (o *roleResourceType) List(ctx context.Context, parentId *v2.ResourceId, token *pagination.Token) ([]*v2.Resource, string, annotations.Annotations, error) { + var rv []*v2.Resource - for _, privilege := range privileges { + for _, privilege := range defaultPrivileges { + rr, err := roleResource(ctx, privilege, parentId) + if err != nil { + return nil, "", nil, err + } + rv = append(rv, rr) + } + + res, err := o.client.GetPrivileges(ctx) + if err != nil { + return nil, "", nil, err + } + + for _, privilege := range res.Privileges { rr, err := roleResource(ctx, privilege, parentId) if err != nil { return nil, "", nil, err @@ -85,6 +101,7 @@ func (o *roleResourceType) Entitlements(_ context.Context, resource *v2.Resource func (o *roleResourceType) Grants(ctx context.Context, resource *v2.Resource, token *pagination.Token) ([]*v2.Grant, string, annotations.Annotations, error) { var rv []*v2.Grant + isCustomPrivilege := !slices.Contains(defaultPrivileges, resource.Id.Resource) userAccounts, groups, err := o.client.GetAccounts(ctx) if err != nil { return nil, "", nil, err @@ -97,9 +114,19 @@ func (o *roleResourceType) Grants(ctx context.Context, resource *v2.Resource, to return nil, "", nil, err } - if resource.Id.Resource == group.PrivilegeSet { - privilegeGrant := grant.NewGrant(resource, memberEntitlement, gr.Id) - rv = append(rv, privilegeGrant) + if !isCustomPrivilege { + if resource.Id.Resource == group.PrivilegeSet { + privilegeGrant := grant.NewGrant(resource, memberEntitlement, gr.Id) + rv = append(rv, privilegeGrant) + } + } else { + for _, privilege := range group.Privileges.JSSObjects { + if resource.Id.Resource == privilege { + privilegeGrant := grant.NewGrant(resource, memberEntitlement, gr.Id) + rv = append(rv, privilegeGrant) + break + } + } } } @@ -110,12 +137,31 @@ func (o *roleResourceType) Grants(ctx context.Context, resource *v2.Resource, to return nil, "", nil, err } - if resource.Id.Resource == userAccount.PrivilegeSet { - privilegeGrant := grant.NewGrant(resource, memberEntitlement, gr.Id) - rv = append(rv, privilegeGrant) + if !isCustomPrivilege { + if resource.Id.Resource == userAccount.PrivilegeSet { + // __AUTO_GENERATED_PRINTF_START__ + fmt.Println("Grants 1") // __AUTO_GENERATED_PRINTF_END__ + privilegeGrant := grant.NewGrant(resource, memberEntitlement, gr.Id) + rv = append(rv, privilegeGrant) + } + } else { + for _, privilege := range userAccount.Privileges.JSSObjects { + // __AUTO_GENERATED_PRINT_VAR_START__ + fmt.Println(fmt.Sprintf("Grants privilege: %+v", privilege)) // __AUTO_GENERATED_PRINT_VAR_END__ + // __AUTO_GENERATED_PRINT_VAR_START__ + fmt.Println(fmt.Sprintf("Grants resource.Id.Resource: %+v", resource.Id.Resource)) // __AUTO_GENERATED_PRINT_VAR_END__ + if resource.Id.Resource == privilege { + // __AUTO_GENERATED_PRINTF_START__ + fmt.Println("Grants 4") // __AUTO_GENERATED_PRINTF_END__ + privilegeGrant := grant.NewGrant(resource, memberEntitlement, gr.Id) + rv = append(rv, privilegeGrant) + break + } + } } } - + // __AUTO_GENERATED_PRINT_VAR_START__ + fmt.Println(fmt.Sprintf("Grants rv: %+v", rv)) // __AUTO_GENERATED_PRINT_VAR_END__ return rv, "", nil, nil } diff --git a/pkg/jamf/client.go b/pkg/jamf/client.go index 66689bce..3a586f13 100644 --- a/pkg/jamf/client.go +++ b/pkg/jamf/client.go @@ -28,6 +28,7 @@ const ( userUrlPath = "/JSSResource/users/id/%d" usersUrlPath = "/JSSResource/users" keepAliveUrlPath = "/api/v1/auth/keep-alive" + privilegesUrlPath = "/api/v1/api-role-privileges" ) type Client struct { @@ -430,3 +431,17 @@ func logBody(body []byte, size int) string { } return string(body) } + +func (c *Client) GetPrivileges(ctx context.Context) (*PrivilegesResponse, error) { + url, err := c.getUrl(privilegesUrlPath) + if err != nil { + return nil, err + } + + var target PrivilegesResponse + if err := c.doRequest(ctx, url, &target); err != nil { + return nil, err + } + + return &target, nil +} diff --git a/pkg/jamf/models.go b/pkg/jamf/models.go index 3a72b309..ff9e6567 100644 --- a/pkg/jamf/models.go +++ b/pkg/jamf/models.go @@ -25,20 +25,26 @@ type BaseAccount struct { // UserAccount - user that has access to their system and can be granted permissions. type UserAccount struct { BaseType - FullName string `json:"full_name"` - Email string `json:"email"` - EmailAddress string `json:"email_address"` - Enabled string `json:"enabled"` - AccessLevel string `json:"access_level"` - PrivilegeSet string `json:"privilege_set"` - Site BaseType `json:"site"` + FullName string `json:"full_name"` + Email string `json:"email"` + EmailAddress string `json:"email_address"` + Enabled string `json:"enabled"` + AccessLevel string `json:"access_level"` + PrivilegeSet string `json:"privilege_set"` + Privileges Privileges `json:"privileges"` + Site BaseType `json:"site"` +} + +type Privileges struct { + JSSObjects []string `json:"jss_objects"` } type Group struct { BaseType - AccessLevel string `json:"access_level"` - PrivilegeSet string `json:"privilege_set"` - Site BaseType `json:"site"` + AccessLevel string `json:"access_level"` + PrivilegeSet string `json:"privilege_set"` + Privileges Privileges `json:"privileges"` + Site BaseType `json:"site"` Members []struct { User BaseType `json:"user"` } `json:"members"` @@ -111,3 +117,7 @@ type AccountsResponse struct { type SitesResponse struct { Sites []Site `json:"sites"` } + +type PrivilegesResponse struct { + Privileges []string `json:"privileges"` +} From b8d9e8f58824156dc5e3c7b0e21fa720f91f5806 Mon Sep 17 00:00:00 2001 From: Alejandro Bernal Date: Mon, 31 Mar 2025 11:00:10 -0500 Subject: [PATCH 2/3] add dynamic roles and improve code readability --- pkg/connector/role.go | 63 ++++++++++++++----------------------------- pkg/jamf/models.go | 12 ++++++++- 2 files changed, 31 insertions(+), 44 deletions(-) diff --git a/pkg/connector/role.go b/pkg/connector/role.go index 744a2859..b5aba6d9 100644 --- a/pkg/connector/role.go +++ b/pkg/connector/role.go @@ -23,9 +23,7 @@ func (o *roleResourceType) ResourceType(_ context.Context) *v2.ResourceType { return o.resourceType } -const CustomPrivilege = "Custom" - -var defaultPrivileges = []string{ +var privilegeSets = []string{ "Administrator", "Auditor", "Enrollment Only", @@ -59,8 +57,8 @@ func roleResource(ctx context.Context, role string, parentResourceID *v2.Resourc func (o *roleResourceType) List(ctx context.Context, parentId *v2.ResourceId, token *pagination.Token) ([]*v2.Resource, string, annotations.Annotations, error) { var rv []*v2.Resource - for _, privilege := range defaultPrivileges { - rr, err := roleResource(ctx, privilege, parentId) + for _, privilegeSet := range privilegeSets { + rr, err := roleResource(ctx, privilegeSet, parentId) if err != nil { return nil, "", nil, err } @@ -100,8 +98,7 @@ func (o *roleResourceType) Entitlements(_ context.Context, resource *v2.Resource func (o *roleResourceType) Grants(ctx context.Context, resource *v2.Resource, token *pagination.Token) ([]*v2.Grant, string, annotations.Annotations, error) { var rv []*v2.Grant - - isCustomPrivilege := !slices.Contains(defaultPrivileges, resource.Id.Resource) + isCustomPrivilege := !slices.Contains(privilegeSets, resource.Id.Resource) userAccounts, groups, err := o.client.GetAccounts(ctx) if err != nil { return nil, "", nil, err @@ -114,19 +111,14 @@ func (o *roleResourceType) Grants(ctx context.Context, resource *v2.Resource, to return nil, "", nil, err } - if !isCustomPrivilege { - if resource.Id.Resource == group.PrivilegeSet { - privilegeGrant := grant.NewGrant(resource, memberEntitlement, gr.Id) - rv = append(rv, privilegeGrant) - } - } else { - for _, privilege := range group.Privileges.JSSObjects { - if resource.Id.Resource == privilege { - privilegeGrant := grant.NewGrant(resource, memberEntitlement, gr.Id) - rv = append(rv, privilegeGrant) - break - } - } + if isCustomPrivilege && slices.Contains(group.Privileges.JSSObjects, resource.Id.Resource) { + privilegeGrant := grant.NewGrant(resource, memberEntitlement, gr.Id) + rv = append(rv, privilegeGrant) + continue + } + if group.PrivilegeSet == resource.Id.Resource { + privilegeGrant := grant.NewGrant(resource, memberEntitlement, gr.Id) + rv = append(rv, privilegeGrant) } } @@ -137,31 +129,16 @@ func (o *roleResourceType) Grants(ctx context.Context, resource *v2.Resource, to return nil, "", nil, err } - if !isCustomPrivilege { - if resource.Id.Resource == userAccount.PrivilegeSet { - // __AUTO_GENERATED_PRINTF_START__ - fmt.Println("Grants 1") // __AUTO_GENERATED_PRINTF_END__ - privilegeGrant := grant.NewGrant(resource, memberEntitlement, gr.Id) - rv = append(rv, privilegeGrant) - } - } else { - for _, privilege := range userAccount.Privileges.JSSObjects { - // __AUTO_GENERATED_PRINT_VAR_START__ - fmt.Println(fmt.Sprintf("Grants privilege: %+v", privilege)) // __AUTO_GENERATED_PRINT_VAR_END__ - // __AUTO_GENERATED_PRINT_VAR_START__ - fmt.Println(fmt.Sprintf("Grants resource.Id.Resource: %+v", resource.Id.Resource)) // __AUTO_GENERATED_PRINT_VAR_END__ - if resource.Id.Resource == privilege { - // __AUTO_GENERATED_PRINTF_START__ - fmt.Println("Grants 4") // __AUTO_GENERATED_PRINTF_END__ - privilegeGrant := grant.NewGrant(resource, memberEntitlement, gr.Id) - rv = append(rv, privilegeGrant) - break - } - } + if isCustomPrivilege && slices.Contains(userAccount.Privileges.JSSObjects, resource.Id.Resource) { + privilegeGrant := grant.NewGrant(resource, memberEntitlement, gr.Id) + rv = append(rv, privilegeGrant) + continue + } + if userAccount.PrivilegeSet == resource.Id.Resource { + privilegeGrant := grant.NewGrant(resource, memberEntitlement, gr.Id) + rv = append(rv, privilegeGrant) } } - // __AUTO_GENERATED_PRINT_VAR_START__ - fmt.Println(fmt.Sprintf("Grants rv: %+v", rv)) // __AUTO_GENERATED_PRINT_VAR_END__ return rv, "", nil, nil } diff --git a/pkg/jamf/models.go b/pkg/jamf/models.go index ff9e6567..d47b58a0 100644 --- a/pkg/jamf/models.go +++ b/pkg/jamf/models.go @@ -36,12 +36,22 @@ type UserAccount struct { } type Privileges struct { + // array of privileges the resource has access to JSSObjects []string `json:"jss_objects"` } type Group struct { BaseType - AccessLevel string `json:"access_level"` + AccessLevel string `json:"access_level"` + // PrivilegeSet can take the following values: + // + // - "Administrator" + // + // - "Auditor" + // + // - "Enrollment Only" + // + // - "Custom" PrivilegeSet string `json:"privilege_set"` Privileges Privileges `json:"privileges"` Site BaseType `json:"site"` From 4b8054e4fefa468de6270f3a32b037129f396e86 Mon Sep 17 00:00:00 2001 From: John Allers Date: Tue, 8 Apr 2025 08:44:38 -0400 Subject: [PATCH 3/3] fix whitespace --- pkg/connector/role.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/connector/role.go b/pkg/connector/role.go index b5aba6d9..685ec5c2 100644 --- a/pkg/connector/role.go +++ b/pkg/connector/role.go @@ -55,7 +55,6 @@ func roleResource(ctx context.Context, role string, parentResourceID *v2.Resourc } func (o *roleResourceType) List(ctx context.Context, parentId *v2.ResourceId, token *pagination.Token) ([]*v2.Resource, string, annotations.Annotations, error) { - var rv []*v2.Resource for _, privilegeSet := range privilegeSets { rr, err := roleResource(ctx, privilegeSet, parentId)