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
1 change: 1 addition & 0 deletions pkg/config/conf.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@ var (
field.WithDescription("Organization of your github app"),
field.WithRequired(true),
)

// syncLastActivity is hidden from this connector's GUI config and --help
// since it only applies to GitHub Enterprise audit-log access.
// baton-github-enterprise sets it directly on the shared Github struct,
// bypassing this CLI layer, so hiding it here doesn't affect that connector.
syncLastActivity = field.BoolField(
"sync-last-activity",
field.WithDisplayName("Sync users last activity"),
field.WithDescription("See when members were last active in your organizations."),
field.WithHidden(true),
field.WithExportTarget(field.ExportTargetCLIOnly),
)
)

//go:generate go run ./gen
Expand All @@ -107,6 +119,7 @@ var Config = field.NewConfiguration(
syncSecrets,
omitArchivedRepositories,
directCollaboratorsOnly,
syncLastActivity,
},
field.WithConnectorDisplayName("GitHub v2"),
field.WithHelpUrl("/docs/baton/github-v2"),
Expand Down
20 changes: 19 additions & 1 deletion pkg/connector/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ type GitHub struct {
omitArchivedRepositories bool
directCollaboratorsOnly bool
enterprises []string
syncLastActivity bool
}

func (gh *GitHub) ResourceSyncers(ctx context.Context) []connectorbuilder.ResourceSyncerV2 {
Expand All @@ -148,6 +149,11 @@ func (gh *GitHub) ResourceSyncers(ctx context.Context) []connectorbuilder.Resour
resourceSyncers = append(resourceSyncers, APITokenBuilder(gh.client, gh.orgCache))
}

if gh.syncLastActivity {

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.

Non-blocking suggestion — fine as a follow-up, not a condition for this PR.

usageAppBuilder is registered only while syncLastActivity is true, so flipping the enterprise toggle from true to false drops the whole usage-app resource type out of ResourceSyncers(), and C1 reads a previously synced type disappearing as a deletion. Registering the builder unconditionally and gating only EventFeeds() would keep the resource catalog stable across a config change.

Consumer config: https://github.com/ConductorOne/baton-github-enterprise/blob/52c1fbed32d34b623bb5aa80afb655b01714b9ee/pkg/config/config.go#L80-L113

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Deliberate tradeoff, not planning to change it: this entitlement never has grants (Grants() returns nil, resource type carries SkipGrants), so a toggle-triggered "deletion" is just catalog churn, not an access change. Registering it unconditionally would instead give every plain baton-github customer (sync-last-activity is hidden/CLI-only there) a permanent, unexplained "GitHub / access" resource with no function. Gating avoids that at the cost of occasional harmless churn for the few admins who actually flip the enterprise toggle.

// usageAppBuilder only exists to support usageEventFeed, so it's gated the same way.
resourceSyncers = append(resourceSyncers, newUsageAppBuilder())
}

if len(gh.enterprises) > 0 {
resourceSyncers = append(resourceSyncers,
EnterpriseRoleBuilder(gh.client, gh.appClient, gh.customClient, gh.enterprises),
Expand All @@ -157,8 +163,18 @@ func (gh *GitHub) ResourceSyncers(ctx context.Context) []connectorbuilder.Resour
return resourceSyncers
}

func (gh *GitHub) EventFeeds(_ context.Context) []connectorbuilder.EventFeed {
if !gh.syncLastActivity {

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.

Non-blocking suggestion — worth a follow-up once the enterprise side lands.

The feat/cxp-383 branch wires SyncLastActivity, but DefaultCapabilitiesBuilder has no EventFeeds() and its generated capabilities contain neither usage-app nor CAPABILITY_EVENT_FEEDS; both new constructors here are private, so downstream cannot reuse them. Exporting or sharing the feed/resource capability path (and regenerating the enterprise metadata) would let the consumer advertise the feed.

Consumer builder: https://github.com/ConductorOne/baton-github-enterprise/blob/52c1fbed32d34b623bb5aa80afb655b01714b9ee/pkg/connector/connector.go#L31-L62

return nil
}

return []connectorbuilder.EventFeed{
newUsageEventFeed(gh.client, gh.orgs),
}
}

// Metadata returns metadata about the connector.
func (gh *GitHub) Metadata(ctx context.Context) (*v2.ConnectorMetadata, error) {
func (gh *GitHub) Metadata(_ context.Context) (*v2.ConnectorMetadata, error) {
return &v2.ConnectorMetadata{
DisplayName: "GitHub",
AccountCreationSchema: &v2.ConnectorAccountCreationSchema{
Expand Down Expand Up @@ -346,6 +362,7 @@ func newWithGithubPAT(ctx context.Context, ghc *cfg.Github) (*GitHub, error) {
syncSecrets: ghc.SyncSecrets,
omitArchivedRepositories: ghc.OmitArchivedRepositories,
directCollaboratorsOnly: ghc.DirectCollaboratorsOnly,
syncLastActivity: ghc.SyncLastActivity,
Comment thread
JavierCarnelli-ConductorOne marked this conversation as resolved.
}, nil
}

Expand Down Expand Up @@ -452,6 +469,7 @@ func newWithGithubApp(ctx context.Context, ghc *cfg.Github) (*GitHub, error) {
syncSecrets: ghc.SyncSecrets,
omitArchivedRepositories: ghc.OmitArchivedRepositories,
directCollaboratorsOnly: ghc.DirectCollaboratorsOnly,
syncLastActivity: ghc.SyncLastActivity,
}
return gh, nil
}
Expand Down
64 changes: 64 additions & 0 deletions pkg/connector/usage_app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package connector

import (
"context"

v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
"github.com/conductorone/baton-sdk/pkg/annotations"
"github.com/conductorone/baton-sdk/pkg/types/entitlement"
resourceSdk "github.com/conductorone/baton-sdk/pkg/types/resource"
)

const (
usageAppResourceID = "github"
usageAppDisplayName = "GitHub Activity"
usageAppAccessEntitlement = "access"
)

// resourceTypeUsageApp is a synthetic TRAIT_APP resource that usageEventFeed's
// UsageEvents target, since GitHub's real resource types carry no entitlement
// C1's usage uplift can key off of. Only synced when sync-last-activity is on.
var resourceTypeUsageApp = &v2.ResourceType{
Id: "usage-app",
DisplayName: "GitHub Activity",
Traits: []v2.ResourceType_Trait{v2.ResourceType_TRAIT_APP},
Annotations: annotations.New(&v2.SkipGrants{}),
}

// usageAppBuilder syncs a single static App resource for usageEventFeed's
// UsageEvents to target.
type usageAppBuilder struct{}

func newUsageAppBuilder() *usageAppBuilder {
return &usageAppBuilder{}
}

func (b *usageAppBuilder) ResourceType(_ context.Context) *v2.ResourceType {
return resourceTypeUsageApp
}

func (b *usageAppBuilder) List(_ context.Context, _ *v2.ResourceId, _ resourceSdk.SyncOpAttrs) ([]*v2.Resource, *resourceSdk.SyncOpResults, error) {
res, err := resourceSdk.NewAppResource(usageAppDisplayName, resourceTypeUsageApp, usageAppResourceID, nil)
if err != nil {
return nil, nil, err
}
return []*v2.Resource{res}, &resourceSdk.SyncOpResults{}, nil
}

func (b *usageAppBuilder) Entitlements(_ context.Context, resource *v2.Resource, _ resourceSdk.SyncOpAttrs) ([]*v2.Entitlement, *resourceSdk.SyncOpResults, error) {
return []*v2.Entitlement{
entitlement.NewAssignmentEntitlement(
resource,
usageAppAccessEntitlement,
entitlement.WithGrantableTo(resourceTypeUser),
entitlement.WithDisplayName("GitHub Access"),
entitlement.WithDescription("Has access to GitHub"),
),
}, &resourceSdk.SyncOpResults{}, nil
}

func (b *usageAppBuilder) Grants(_ context.Context, _ *v2.Resource, _ resourceSdk.SyncOpAttrs) ([]*v2.Grant, *resourceSdk.SyncOpResults, error) {
// Grants are intentionally not emitted: the usage uplift maps the login
// actor directly to a synced app user, not via a grant.
return nil, &resourceSdk.SyncOpResults{}, nil
Comment thread
JavierCarnelli-ConductorOne marked this conversation as resolved.
}
Loading
Loading