Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions pkg/connector/agents.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,26 @@ func (o *agentBuilder) ResourceType(ctx context.Context) *v2.ResourceType {
}

// agentStatus maps the client agent-status string to the proto enum.
func agentStatus(s string) v2.AgentTrait_AgentStatus {
func agentStatus(s string) v2.Status_ResourceStatus {
switch s {
case client.AgentStatusReady:
return v2.AgentTrait_AGENT_STATUS_READY
return v2.Status_RESOURCE_STATUS_ENABLED
case client.AgentStatusDisabled:
return v2.AgentTrait_AGENT_STATUS_DISABLED
return v2.Status_RESOURCE_STATUS_DISABLED
case client.AgentStatusDeleted:
return v2.AgentTrait_AGENT_STATUS_DELETED
return v2.Status_RESOURCE_STATUS_DELETED
default:
return v2.AgentTrait_AGENT_STATUS_UNSPECIFIED
return v2.Status_RESOURCE_STATUS_UNSPECIFIED
}
}

func agentResource(a *client.Agent, parentResourceID *v2.ResourceId) (*v2.Resource, error) {
profile := make(map[string]interface{}, len(a.Profile))
profile := make(map[string]any, len(a.Profile))
for k, v := range a.Profile {
profile[k] = v
}

agentOpts := []resource.AgentTraitOption{
resource.WithAgentStatus(agentStatus(a.Status)),
resource.WithAgentProfile(profile),
}
agentOpts := []resource.AgentTraitOption{}
// The identity the agent authenticates as (a service-account user).
if a.IdentityID != "" {
identityID, err := resource.NewResourceID(userResourceType, a.IdentityID)
Expand All @@ -59,6 +56,9 @@ func agentResource(a *client.Agent, parentResourceID *v2.ResourceId) (*v2.Resour
a.Id,
resource.WithAgentTrait(agentOpts...),
resource.WithParentResourceID(parentResourceID),
resource.WithResourceStatus(agentStatus(a.Status), a.Status),
resource.WithResourceProfile(profile),
resource.WithResourceCreatedAt(a.CreatedAt),
)
}

Expand Down
5 changes: 3 additions & 2 deletions pkg/connector/connector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func TestSecretsEmitSecretTrait(t *testing.T) {
st := pickSecretTrait(t, r)
assert.NotEqual(t, v2.SecretTrait_CREDENTIAL_TYPE_UNSPECIFIED, st.GetCredentialType())
credTypes[st.GetCredentialType()]++
require.NotNil(t, st.GetCreatedAt())
require.NotNil(t, r.GetCreatedAt())
if st.GetIdentityId() != nil {
owned++
// Owned secrets back-reference a user (service account) resource.
Expand Down Expand Up @@ -194,7 +194,8 @@ func TestAgentsEmitAgentTrait(t *testing.T) {
assert.Equal(t, agentResourceType.Id, r.GetId().GetResourceType())
at, err := resource.GetAgentTrait(r)
require.NoError(t, err)
assert.NotEqual(t, v2.AgentTrait_AGENT_STATUS_UNSPECIFIED, at.GetStatus())
require.NotNil(t, r.GetStatus())
assert.NotEqual(t, v2.Status_RESOURCE_STATUS_UNSPECIFIED, r.GetStatus().GetStatus())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: The bulk of this PR moves profile off the user/group/role/app traits onto Resource.profile, but the only new assertions cover agent status and secret created_at. Nothing asserts r.GetProfile() is populated for users, groups, roles, or NHIs, so a dropped profile in that migration would pass CI silently. Consider adding a require.NotNil(t, r.GetProfile()) (plus a key spot-check) in the user/group/role/NHI tests.

// Each agent authenticates as a service-account user.
require.NotNil(t, at.GetIdentityResourceId())
assert.Equal(t, userResourceType.Id, at.GetIdentityResourceId().GetResourceType())
Expand Down
6 changes: 4 additions & 2 deletions pkg/connector/groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,14 @@ func groupResource(g *client.Group, parentResourceID *v2.ResourceId) (*v2.Resour
profile["created_at"] = g.CreatedAt.Format(time.RFC3339)
profile["updated_at"] = g.UpdatedAt.Format(time.RFC3339)

return resource.NewGroupResource(
return resource.NewResource(
g.Name,
groupResourceType,
g.Id,
[]resource.GroupTraitOption{resource.WithGroupProfile(profile)},
resource.WithGroupTrait(),
resource.WithParentResourceID(parentResourceID),
resource.WithResourceProfile(profile),
resource.WithResourceCreatedAt(g.CreatedAt),
)
}

Expand Down
11 changes: 5 additions & 6 deletions pkg/connector/nhis.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,15 @@ func nhiResource(n *client.NHI, rt *v2.ResourceType, parentResourceID *v2.Resour
opts := []resource.ResourceOption{
resource.WithParentResourceID(parentResourceID),
resource.WithNHIType(nhiType(n.NhiType), n.NhiDetail),
resource.WithResourceProfile(map[string]any{
"nhi_type": n.NhiType,
}),
}
switch n.Kind {
case client.NHIKindRole:
opts = append(opts, resource.WithRoleTrait(resource.WithRoleProfile(map[string]interface{}{
"nhi_type": n.NhiType,
})))
opts = append(opts, resource.WithRoleTrait())
default:
opts = append(opts, resource.WithAppTrait(resource.WithAppProfile(map[string]interface{}{
"nhi_type": n.NhiType,
})))
opts = append(opts, resource.WithAppTrait())
}
return resource.NewResource(n.Name, rt, n.Id, opts...)
}
Expand Down
29 changes: 19 additions & 10 deletions pkg/connector/roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/conductorone/baton-demo/pkg/client"
v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
"github.com/conductorone/baton-sdk/pkg/annotations"
"github.com/conductorone/baton-sdk/pkg/connectorbuilder"
"github.com/conductorone/baton-sdk/pkg/pagination"
sdkEntitlement "github.com/conductorone/baton-sdk/pkg/types/entitlement"
sdkGrant "github.com/conductorone/baton-sdk/pkg/types/grant"
Expand All @@ -25,22 +26,30 @@ type roleBuilder struct {
client *client.Client
}

var _ connectorbuilder.ResourceSyncerV2 = &roleBuilder{}

func (o *roleBuilder) ResourceType(ctx context.Context) *v2.ResourceType {
return roleResourceType
}

func roleResource(r *client.Role, parentResourceID *v2.ResourceId) (*v2.Resource, error) {
traits := []resource.RoleTraitOption{
resource.WithRoleProfile(map[string]any{
"role_color": "blue",
"total_assignments": len(r.DirectAssignments) + len(r.GroupAssignments),
"direct_assignments": len(r.DirectAssignments),
"group_assignments": len(r.GroupAssignments),
"created_at": r.CreatedAt.Format(time.RFC3339),
"updated_at": r.UpdatedAt.Format(time.RFC3339),
}),
profile := map[string]any{
"role_color": "blue",
"total_assignments": len(r.DirectAssignments) + len(r.GroupAssignments),
"direct_assignments": len(r.DirectAssignments),
"group_assignments": len(r.GroupAssignments),
"created_at": r.CreatedAt.Format(time.RFC3339),
"updated_at": r.UpdatedAt.Format(time.RFC3339),
}
return resource.NewRoleResource(r.Name, roleResourceType, r.Id, traits, resource.WithParentResourceID(parentResourceID))
return resource.NewResource(
r.Name,
roleResourceType,
r.Id,
resource.WithRoleTrait(),
resource.WithParentResourceID(parentResourceID),
resource.WithResourceProfile(profile),
resource.WithResourceCreatedAt(r.CreatedAt),
)
}

// List returns all the roles from the database as resource objects
Expand Down
6 changes: 3 additions & 3 deletions pkg/connector/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ func credentialType(s string) v2.SecretTrait_CredentialType {
func secretResource(s *client.Secret, parentResourceID *v2.ResourceId) (*v2.Resource, error) {
traitOpts := []resource.SecretTraitOption{
resource.WithSecretType(credentialType(s.CredentialType)),
resource.WithSecretCreatedAt(s.CreatedAt),
}
if s.CredentialDetail != "" {
traitOpts = append(traitOpts, resource.WithSecretDetail(s.CredentialDetail))
Expand All @@ -57,12 +56,13 @@ func secretResource(s *client.Secret, parentResourceID *v2.ResourceId) (*v2.Reso
traitOpts = append(traitOpts, resource.WithSecretIdentityID(identityID))
}

return resource.NewSecretResource(
return resource.NewResource(
s.Name,
secretResourceType,
s.Id,
traitOpts,
resource.WithSecretTrait(traitOpts...),
resource.WithParentResourceID(parentResourceID),
resource.WithResourceCreatedAt(s.CreatedAt),
)
}

Expand Down
30 changes: 14 additions & 16 deletions pkg/connector/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,15 @@ func accountType(s string) v2.UserTrait_AccountType {
}
}

func userResource(u *client.User, parentResourceID *v2.ResourceId) (*v2.Resource, error) {
// Determine the user status based on the enabled field
var status v2.UserTrait_Status_Status
var statusMessage string
if u.Enabled {
status = v2.UserTrait_Status_STATUS_ENABLED
statusMessage = "Enabled"
} else {
status = v2.UserTrait_Status_STATUS_DISABLED
statusMessage = "Disabled"
func userStatus(enabled bool) (v2.Status_ResourceStatus, string) {
if enabled {
return v2.Status_RESOURCE_STATUS_ENABLED, "Enabled"
}
return v2.Status_RESOURCE_STATUS_DISABLED, "Disabled"
}

func userResource(u *client.User, parentResourceID *v2.ResourceId) (*v2.Resource, error) {
status, statusMessage := userStatus(u.Enabled)

attrs := make(map[string]any)
for k, v := range u.Attrs {
Expand All @@ -120,21 +118,21 @@ func userResource(u *client.User, parentResourceID *v2.ResourceId) (*v2.Resource
attrs["created_at"] = u.CreatedAt.Format(time.RFC3339)
attrs["updated_at"] = u.UpdatedAt.Format(time.RFC3339)

traits := []resource.UserTraitOption{
userOpts := []resource.UserTraitOption{
resource.WithEmail(u.Email, true),
resource.WithUserLogin(u.Id),
resource.WithDetailedStatus(status, statusMessage),
resource.WithEmployeeID(u.Id),
resource.WithAccountType(accountType(u.AccountType)),
resource.WithUserProfile(attrs),
resource.WithCreatedAt(u.CreatedAt),
}
return resource.NewUserResource(
return resource.NewResource(
u.Name,
userResourceType,
u.Id,
traits,
resource.WithUserTrait(userOpts...),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: Dropping WithDetailedStatus means NewUserTrait applies its own default — UserTrait.status = STATUS_ENABLED — for every user, and the SDK only syncs trait→resource (syncUserTraitToResource), never resource→trait. So a disabled user now emits resource status DISABLED (correct) alongside a deprecated trait status of ENABLED (actively wrong, not merely absent). Contrast agents.go, where WithAgentTrait() leaves the trait status UNSPECIFIED and is therefore safe. If any consumer still reads the deprecated UserTrait.status during the deprecation window, disabled accounts will look active; consider keeping resource.WithDetailedStatus(...) with a //nolint:staticcheck until readers have migrated.

resource.WithParentResourceID(parentResourceID),
resource.WithResourceStatus(status, statusMessage),
resource.WithResourceProfile(attrs),
resource.WithResourceCreatedAt(u.CreatedAt),
)
}

Expand Down
Loading