-
Notifications
You must be signed in to change notification settings - Fork 4
CXP-383 Support Last Log In w/ usage event feed for the Enterprise connector #188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
95053cd
fc29653
a16e4cb
efd6e70
e4611ac
e0fcb75
348ca52
7d202a7
99f22eb
f9125cd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -127,6 +127,7 @@ type GitHub struct { | |
| omitArchivedRepositories bool | ||
| directCollaboratorsOnly bool | ||
| enterprises []string | ||
| syncLastActivity bool | ||
| } | ||
|
|
||
| func (gh *GitHub) ResourceSyncers(ctx context.Context) []connectorbuilder.ResourceSyncerV2 { | ||
|
|
@@ -148,6 +149,11 @@ func (gh *GitHub) ResourceSyncers(ctx context.Context) []connectorbuilder.Resour | |
| resourceSyncers = append(resourceSyncers, APITokenBuilder(gh.client, gh.orgCache)) | ||
| } | ||
|
|
||
| if gh.syncLastActivity { | ||
| // 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), | ||
|
|
@@ -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 { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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{ | ||
|
|
@@ -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, | ||
|
JavierCarnelli-ConductorOne marked this conversation as resolved.
|
||
| }, nil | ||
| } | ||
|
|
||
|
|
@@ -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 | ||
| } | ||
|
|
||
| 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 | ||
|
JavierCarnelli-ConductorOne marked this conversation as resolved.
|
||
| } | ||
There was a problem hiding this comment.
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.
usageAppBuilderis registered only whilesyncLastActivityis true, so flipping the enterprise toggle from true to false drops the wholeusage-appresource type out ofResourceSyncers(), and C1 reads a previously synced type disappearing as a deletion. Registering the builder unconditionally and gating onlyEventFeeds()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
There was a problem hiding this comment.
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 carriesSkipGrants), 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.