openhound-github 0.6.7__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openhound_github-0.6.7 → openhound_github-0.7.0}/PKG-INFO +16 -3
- {openhound_github-0.6.7 → openhound_github-0.7.0}/README.md +15 -2
- openhound_github-0.7.0/descriptions/edges/GH_MemberOf.md +3 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/schema.json +1 -1
- openhound_github-0.7.0/src/openhound_github/github_rest_client.py +3 -0
- openhound_github-0.7.0/src/openhound_github/github_retry.py +48 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/helpers.py +6 -6
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/lookup.py +36 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/main.py +2 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/__init__.py +2 -1
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/team.py +44 -1
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/resources/organization.py +236 -6
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/source.py +1 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/transforms.py +33 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_error_resilience.py +1 -1
- openhound_github-0.7.0/tests/test_lookup.py +117 -0
- openhound_github-0.7.0/tests/test_team_external_groups.py +384 -0
- openhound_github-0.6.7/descriptions/edges/GH_MemberOf.md +0 -3
- openhound_github-0.6.7/tests/test_lookup.py +0 -28
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/CODE_OF_CONDUCT.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/CONTRIBUTING.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/GOVERNANCE.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/GitHub-COC-Header.png +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/GitHub-CONTRIB-Header.png +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/GitHub-GOV-Header.png +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/GitHub-Header.png +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/GitHub-LIC-Header.png +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/GitHub-SEC-Header.png +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/SECURITY.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/SO-Dark.svg +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/SO-FOSS-Dark.svg +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/SO-FOSS-Light.svg +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/SO-Light.svg +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/workflows/publish.yml +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/workflows/release-on-merge.yml +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.github/workflows/validate-branch.yml +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.gitignore +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.gitmodules +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/.pre-commit-config.yaml +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/LICENSE.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_AddAssignee.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_AddCollaborator.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_AddLabel.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_AddMember.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_AdminTo.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ApprovesDeploymentTo.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_AssignedTo.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_BypassBranchProtection.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_BypassPullRequestAllowances.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CallsWorkflow.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CanAccess.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CanAssumeIdentity.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CanCreateBranch.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CanCreateEnvironment.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CanCreateRepositoryWithRunnerAccess.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CanDeployToEnvironment.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CanEditEnvironment.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CanEditProtection.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CanPwnRequest.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CanReadSecretScanningAlert.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CanUseRunner.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CanWriteBranch.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CloseDiscussion.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CloseIssue.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ClosePullRequest.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_Contains.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ConvertIssuesToDiscussions.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CreateDiscussionCategory.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CreateRepository.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CreateSoloMergeQueueEntry.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CreateTag.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_CreateTeam.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_DeleteAlertsCodeScanning.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_DeleteDiscussion.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_DeleteDiscussionComment.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_DeleteIssue.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_DeleteTag.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_DependsOn.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_DeploysTo.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_EditCategoryOnDiscussion.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_EditDiscussionCategory.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_EditDiscussionComment.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_EditRepoAnnouncementBanners.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_EditRepoCustomPropertiesValues.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_EditRepoMetadata.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_EditRepoProtections.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_HasBaseRole.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_HasBranch.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_HasEnvironment.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_HasExternalIdentity.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_HasJob.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_HasMember.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_HasPersonalAccessToken.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_HasPersonalAccessTokenRequest.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_HasRole.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_HasRunner.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_HasSamlIdentityProvider.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_HasSecret.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_HasStep.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_HasVariable.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_HasWorkflow.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_InheritedFrom.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_InstalledAs.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_InviteMember.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_IsEligibleFor.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_JumpMergeQueue.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ManageDeployKeys.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ManageDiscussionBadges.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ManageOrganizationWebhooks.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ManageRepoSecurityProducts.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ManageSecurityProducts.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ManageSettingsMergeTypes.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ManageSettingsPages.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ManageSettingsProjects.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ManageSettingsWiki.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ManageTopics.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ManageWebhooks.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_MapsToUser.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_MarkAsDuplicate.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_MatchesEnvironmentPolicy.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_OrgBypassCodeScanningDismissalRequests.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_OrgBypassSecretScanningClosureRequests.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_OrgReviewAndManageSecretScanningBypassRequests.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_OrgReviewAndManageSecretScanningClosureRequests.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_Owns.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ProtectedBy.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_PushProtectedBranch.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ReadCodeScanning.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ReadOrganizationActionsUsageMetrics.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ReadOrganizationCustomOrgRole.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ReadOrganizationCustomRepoRole.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ReadRepoContents.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_RemoveAssignee.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_RemoveLabel.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ReopenDiscussion.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ReopenIssue.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ReopenPullRequest.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_RequestPrReview.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ResolveDependabotAlerts.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ResolveSecretScanningAlerts.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_RestrictionsCanPush.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_RunOrgMigration.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_SetInteractionLimits.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_SetIssueType.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_SetMilestone.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_SetSocialPreview.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_SyncedTo.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ToggleDiscussionAnswer.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ToggleDiscussionCommentMinimize.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_TransferRepository.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_UsesSecret.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_UsesVariable.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ValidToken.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ViewDependabotAlerts.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_ViewSecretScanningAlerts.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_WriteCodeScanning.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_WriteOrganizationActionsSecrets.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_WriteOrganizationActionsSettings.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_WriteOrganizationActionsVariables.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_WriteOrganizationCustomOrgRole.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_WriteOrganizationCustomRepoRole.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_WriteOrganizationNetworkConfigurations.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_WriteRepoContents.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/edges/GH_WriteRepoPullRequests.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_App.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_AppInstallation.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_Branch.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_BranchProtectionRule.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_EnterpriseRunner.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_EnterpriseRunnerGroup.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_Environment.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_EnvironmentBranchPolicy.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_EnvironmentSecret.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_EnvironmentVariable.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_ExternalIdentity.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_OrgRole.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_OrgRunner.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_OrgRunnerGroup.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_OrgSecret.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_OrgVariable.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_Organization.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_PersonalAccessToken.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_PersonalAccessTokenRequest.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_RepoRole.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_RepoRunner.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_RepoSecret.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_RepoVariable.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_Repository.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_SamlIdentityProvider.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_SecretScanningAlert.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_Team.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_TeamRole.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_User.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_Workflow.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_WorkflowJob.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/descriptions/nodes/GH_WorkflowStep.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/docs/og-docs.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/privilege_zone_rules/t0-all-repo-admin-role.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/privilege_zone_rules/t0-app-installations-all-repos.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/privilege_zone_rules/t0-apps-all-repos.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/privilege_zone_rules/t0-external-identities-owners.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/privilege_zone_rules/t0-organizations.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/privilege_zone_rules/t0-owner-users.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/privilege_zone_rules/t0-owners-role.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/privilege_zone_rules/t0-pats-all-repos.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/privilege_zone_rules/t0-privilege-escalation-roles.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/privilege_zone_rules/t0-privilege-escalation-users.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/privilege_zone_rules/t0-saml-identity-providers.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/README.md +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/actions-sha-pinning-not-required.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/active-leaked-secrets.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/advanced-security-disabled-new-repos.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/all-actions-allowed.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/app-installations-all-repos.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/branch-protection-admins-not-enforced.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/branch-protection-deletions-allowed.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/branch-protection-force-pushes.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/branch-protection-no-code-owner-reviews.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/branch-protection-no-pr-reviews.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/branch-protection-no-status-checks.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/branch-protection-self-approval.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/branch-protection-stale-reviews.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/bypass-pr-requirements.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/dangerous-branch-perms.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/default-repository-permissions.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/demo-sso-to-cloud-round-trip.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/dependabot-alerts-disabled-new-repos.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/dependabot-updates-disabled-new-repos.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/dependency-graph-disabled-new-repos.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/environments-admin-bypass.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/expired-pats.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/external-identities-without-scim.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/github-to-azure-identity.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/global-repo-perms.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/hybrid-identities.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/members-can-change-repo-visibility.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/members-can-create-pages.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/members-can-create-public-repos.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/members-can-delete-repos.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/members-can-fork-private-repos.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/members-can-invite-outside-collaborators.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/org-owners.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/orgs-without-2fa.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/pats-all-repo-access.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/pending-pat-requests.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/private-repos-forking-allowed.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/privileged-custom-org-roles.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/privileged-hybrid-identities.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/public-repos.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/push-protection-disabled-new-repos.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/push-to-protected-branches.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/repos-secret-scanning-disabled.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/repos-vulnerable-to-workflow-secret-exfil.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/repository-workflows.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/saml-configuration.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/secret-scanning-alerts.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/secret-scanning-disabled-new-repos.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/secrets-reachable-by-user.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/team-membership-admin.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/team-structure.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/unprotected-branches.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/unprotected-default-branch-with-workflow.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/unprotected-default-branches.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/extension/saved_searches/web-commit-signoff-not-required.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/justfile +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/pyproject.toml +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/main.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/__init__.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/auth.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/extension.yaml +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/graph.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/graphql.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/kinds/edges.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/kinds/nodes.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/actions_permission.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/app_installation.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/branch.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/branch_pr_bypass_allowance.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/branch_protection_rule.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/branch_push_allowance.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/enterprise.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/enterprise_admin.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/enterprise_helpers.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/enterprise_managed_user.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/enterprise_member.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/enterprise_organization.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/enterprise_role.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/enterprise_role_team.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/enterprise_role_user.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/enterprise_team.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/enterprise_team_member.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/enterprise_team_organization.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/enterprise_team_role.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/enterprise_user.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/env_secret.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/env_variable.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/environment.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/environment_branch_policy.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/external_identity.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/org.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/org_role.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/org_role_member.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/org_role_team.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/org_secret.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/org_variable.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/personal_access_token.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/personal_access_token_access.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/personal_access_token_request.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/projected_enterprise_team.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/repo_role_assignment.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/repository.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/repository_role.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/repository_secret.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/repository_variable.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/runner.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/saml_assertion_consumer_service.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/saml_helpers.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/saml_issuer.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/saml_provider.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/saml_service_provider.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/scim_user.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/secret_scanning_alert.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/team_member.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/team_role.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/user.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/workflow.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/workflow_job.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/models/workflow_step.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/resources/__init__.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/resources/enterprise.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/runner_ids.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_app_auth.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_branch_collection_gate.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_branch_protection_traversability.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/actions-permissions.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/actionsecrets.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/app.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/appinstalls.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/branch-policies.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/branch-prot-rules.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/branches.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/custom-roles.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/environments.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/org-pat-request.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/org-pat.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/org-role-teams.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/org-role-users.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/org-roles.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/org.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/pat-repositories.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/repo-collaborators.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/repo-teams.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/repositories.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/scimusers.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/secrets.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/secretscanningalert.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/selected-org-secrets.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/variables.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_data/api/workflows.json +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_enterprise_capabilities.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_enterprise_organization.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_enterprise_resources.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_environment_model.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_environment_resources.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_environment_reviewer_lookup.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_github_app_retry.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_github_rest_client.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_github_retry.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_helpers.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_org_scim_resources.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_org_secret_models.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_repository_rulesets.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_runner_ids.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_runner_models.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_runner_resources.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_saml_helpers.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_saml_models.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_scim_models.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_source_endpoints.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_source_org_canonicalization.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_variable_models.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/tests/test_workflow_model.py +0 -0
- {openhound_github-0.6.7 → openhound_github-0.7.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: openhound-github
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Github collector for OpenHound
|
|
5
5
|
Author-email: "jdreijer@specterops.io" <jdreijer@specterops.io>, Jared Atkinson <jatkinson@specterops.io>
|
|
6
6
|
License-File: LICENSE.md
|
|
@@ -74,10 +74,23 @@ A token with enterprise SCIM access is used to collect both `/scim/v2/enterprise
|
|
|
74
74
|
|
|
75
75
|
Enterprise roles, including the built-in `members` role, are emitted through `GH_HasRole` and granular enterprise capability relationships. Only capability relationships with a confirmed privilege path are traversable; descriptive permissions such as `GH_WriteEnterpriseSso` remain non-traversable.
|
|
76
76
|
|
|
77
|
+
### Optional privileged collection
|
|
78
|
+
|
|
79
|
+
The collector runs successfully with the documented read-only permission set. Some higher-fidelity relationships require optional non-read-only permissions because GitHub exposes the supporting read APIs behind privileged permission names:
|
|
80
|
+
|
|
81
|
+
- Organization `Members: write` enables external group mapping collection for IdP-synced teams, which allows the converter to emit `SCIM_Provisioned` edges from `SCIM_Group` to `GH_Team`. This is useful when the organization uses team synchronization with an external identity provider; if no GitHub teams are linked to external groups, this permission does not add graph data.
|
|
82
|
+
- Classic PAT scope `manage_runners:enterprise` enables enterprise self-hosted runner group and runner collection. This is useful when the enterprise has enterprise-scoped runner groups or runners, especially runner groups shared into organizations; if all runners are organization- or repository-scoped, this scope does not add graph data.
|
|
83
|
+
|
|
84
|
+
If these optional permissions are not granted, OpenHound skips the affected resources and continues collecting the rest of the GitHub environment.
|
|
85
|
+
|
|
77
86
|
[](#about)
|
|
78
87
|
|
|
79
88
|
## Getting Started
|
|
80
89
|
|
|
81
|
-
Follow the OpenHound docs to get started:
|
|
90
|
+
Follow the OpenHound GitHub collector docs to get started:
|
|
82
91
|
|
|
83
|
-
- [
|
|
92
|
+
- [GitHub collector overview](https://bloodhound.specterops.io/openhound/collectors/github/overview)
|
|
93
|
+
- [Configure the collector](https://bloodhound.specterops.io/openhound/collectors/github/collect-data)
|
|
94
|
+
- [Configure an organization GitHub App](https://bloodhound.specterops.io/openhound/collectors/github/configure-app)
|
|
95
|
+
- [Configure an enterprise GitHub App](https://bloodhound.specterops.io/openhound/collectors/github/configure-enterprise-app)
|
|
96
|
+
- [Configure a personal access token](https://bloodhound.specterops.io/openhound/collectors/github/configure-pat)
|
|
@@ -63,10 +63,23 @@ A token with enterprise SCIM access is used to collect both `/scim/v2/enterprise
|
|
|
63
63
|
|
|
64
64
|
Enterprise roles, including the built-in `members` role, are emitted through `GH_HasRole` and granular enterprise capability relationships. Only capability relationships with a confirmed privilege path are traversable; descriptive permissions such as `GH_WriteEnterpriseSso` remain non-traversable.
|
|
65
65
|
|
|
66
|
+
### Optional privileged collection
|
|
67
|
+
|
|
68
|
+
The collector runs successfully with the documented read-only permission set. Some higher-fidelity relationships require optional non-read-only permissions because GitHub exposes the supporting read APIs behind privileged permission names:
|
|
69
|
+
|
|
70
|
+
- Organization `Members: write` enables external group mapping collection for IdP-synced teams, which allows the converter to emit `SCIM_Provisioned` edges from `SCIM_Group` to `GH_Team`. This is useful when the organization uses team synchronization with an external identity provider; if no GitHub teams are linked to external groups, this permission does not add graph data.
|
|
71
|
+
- Classic PAT scope `manage_runners:enterprise` enables enterprise self-hosted runner group and runner collection. This is useful when the enterprise has enterprise-scoped runner groups or runners, especially runner groups shared into organizations; if all runners are organization- or repository-scoped, this scope does not add graph data.
|
|
72
|
+
|
|
73
|
+
If these optional permissions are not granted, OpenHound skips the affected resources and continues collecting the rest of the GitHub environment.
|
|
74
|
+
|
|
66
75
|
[](#about)
|
|
67
76
|
|
|
68
77
|
## Getting Started
|
|
69
78
|
|
|
70
|
-
Follow the OpenHound docs to get started:
|
|
79
|
+
Follow the OpenHound GitHub collector docs to get started:
|
|
71
80
|
|
|
72
|
-
- [
|
|
81
|
+
- [GitHub collector overview](https://bloodhound.specterops.io/openhound/collectors/github/overview)
|
|
82
|
+
- [Configure the collector](https://bloodhound.specterops.io/openhound/collectors/github/collect-data)
|
|
83
|
+
- [Configure an organization GitHub App](https://bloodhound.specterops.io/openhound/collectors/github/configure-app)
|
|
84
|
+
- [Configure an enterprise GitHub App](https://bloodhound.specterops.io/openhound/collectors/github/configure-enterprise-app)
|
|
85
|
+
- [Configure a personal access token](https://bloodhound.specterops.io/openhound/collectors/github/configure-pat)
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
## General Information
|
|
2
|
+
|
|
3
|
+
The traversable GH_MemberOf edge represents team membership and projection relationships, linking a team role to its parent team, a child team to a parent team in nested team hierarchies, or a GH_EnterpriseTeam to its projected GH_Team in an organization. This edge is traversable because these relationships carry effective team membership context through the graph: a user who holds a role in a child team inherits the repository permissions of ancestor teams, and enterprise-managed team membership flows into the projected organization team.
|
|
@@ -467,7 +467,7 @@
|
|
|
467
467
|
},
|
|
468
468
|
{
|
|
469
469
|
"name": "GH_MemberOf",
|
|
470
|
-
"description": "Team role is a member of a team,
|
|
470
|
+
"description": "Team role is a member of a team, team is nested under a parent team, or enterprise team maps to its projected organization team",
|
|
471
471
|
"is_traversable": true
|
|
472
472
|
},
|
|
473
473
|
{
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from requests import Response
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _response_message(response: Response) -> str:
|
|
7
|
+
try:
|
|
8
|
+
response_data = response.json()
|
|
9
|
+
except ValueError:
|
|
10
|
+
return getattr(response, "text", "")
|
|
11
|
+
if isinstance(response_data, dict):
|
|
12
|
+
return str(response_data.get("message", ""))
|
|
13
|
+
return ""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def is_primary_rate_limit_response(response: Response) -> bool:
|
|
17
|
+
if response.status_code not in (403, 429):
|
|
18
|
+
return False
|
|
19
|
+
|
|
20
|
+
message = _response_message(response).lower()
|
|
21
|
+
return (
|
|
22
|
+
response.headers.get("x-ratelimit-remaining") == "0"
|
|
23
|
+
or "api rate limit exceeded" in message
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def is_secondary_rate_limit_response(response: Response) -> bool:
|
|
28
|
+
if response.status_code not in (403, 429):
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
message = _response_message(response).lower()
|
|
32
|
+
return "secondary rate limit" in message or "abuse detection" in message
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def should_retry_github_response(
|
|
36
|
+
response: Optional[Response], exception: Optional[BaseException]
|
|
37
|
+
) -> bool:
|
|
38
|
+
if response is None:
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
is_primary_rate_limit_response(response)
|
|
43
|
+
or is_secondary_rate_limit_response(response)
|
|
44
|
+
or (
|
|
45
|
+
response.status_code in (403, 429)
|
|
46
|
+
and bool(response.headers.get("Retry-After"))
|
|
47
|
+
)
|
|
48
|
+
)
|
|
@@ -14,6 +14,10 @@ from dlt.sources.helpers.rest_client.paginators import (
|
|
|
14
14
|
from requests import Request
|
|
15
15
|
|
|
16
16
|
from openhound_github.auth import GitHubAppInstallationAuth
|
|
17
|
+
from openhound_github.github_retry import (
|
|
18
|
+
is_primary_rate_limit_response,
|
|
19
|
+
is_secondary_rate_limit_response,
|
|
20
|
+
)
|
|
17
21
|
|
|
18
22
|
logger = logging.getLogger(__name__)
|
|
19
23
|
|
|
@@ -439,18 +443,14 @@ def github_retry_policy(auth: AuthConfigBase):
|
|
|
439
443
|
if response.status_code not in (403, 429):
|
|
440
444
|
return False
|
|
441
445
|
|
|
442
|
-
|
|
443
|
-
if (
|
|
444
|
-
headers.get("x-ratelimit-remaining") == "0"
|
|
445
|
-
or "api rate limit exceeded" in message
|
|
446
|
-
):
|
|
446
|
+
if is_primary_rate_limit_response(response):
|
|
447
447
|
reset_at = headers.get("x-ratelimit-reset")
|
|
448
448
|
delay = int(reset_at) - now if reset_at else 0
|
|
449
449
|
headers["Retry-After"] = str(delay)
|
|
450
450
|
logger.warning("Primary rate limit reached, retrying in %s seconds", delay)
|
|
451
451
|
return True
|
|
452
452
|
|
|
453
|
-
if
|
|
453
|
+
if is_secondary_rate_limit_response(response):
|
|
454
454
|
logger.warning("Secondary rate limit reached, retrying in 60 seconds")
|
|
455
455
|
headers["Retry-After"] = "60"
|
|
456
456
|
return True
|
|
@@ -205,6 +205,42 @@ class GithubLookup(LookupManager):
|
|
|
205
205
|
[org_login, slug],
|
|
206
206
|
)
|
|
207
207
|
|
|
208
|
+
@lru_cache
|
|
209
|
+
def external_group_for_team(
|
|
210
|
+
self, org_login: str, team_database_id: int
|
|
211
|
+
) -> tuple[int, str] | None:
|
|
212
|
+
row = self._find_single_row(
|
|
213
|
+
f"""
|
|
214
|
+
SELECT external_group_id, external_group_name
|
|
215
|
+
FROM {self.schema}.team_external_groups
|
|
216
|
+
WHERE org_login = ?
|
|
217
|
+
AND team_database_id = ?
|
|
218
|
+
""",
|
|
219
|
+
[org_login, team_database_id],
|
|
220
|
+
)
|
|
221
|
+
if row is None:
|
|
222
|
+
return None
|
|
223
|
+
return int(row[0]), str(row[1])
|
|
224
|
+
|
|
225
|
+
@lru_cache
|
|
226
|
+
def scim_group_id_for_team_external_group(
|
|
227
|
+
self, org_login: str, external_group_name: str
|
|
228
|
+
) -> str | None:
|
|
229
|
+
rows = self._find_all_objects(
|
|
230
|
+
f"""
|
|
231
|
+
SELECT DISTINCT esg.id
|
|
232
|
+
FROM {self.schema}.enterprise_scim_groups esg
|
|
233
|
+
JOIN {self.schema}.enterprise_organizations eo
|
|
234
|
+
ON eo.enterprise_node_id = esg.enterprise_node_id
|
|
235
|
+
WHERE eo.login = ?
|
|
236
|
+
AND esg.display_name = ?
|
|
237
|
+
""",
|
|
238
|
+
[org_login, external_group_name],
|
|
239
|
+
)
|
|
240
|
+
if not rows or len(rows) != 1:
|
|
241
|
+
return None
|
|
242
|
+
return str(rows[0][0])
|
|
243
|
+
|
|
208
244
|
@lru_cache
|
|
209
245
|
def external_identity_id_for_guid(
|
|
210
246
|
self, guid: str, environment_slug: str
|
|
@@ -61,12 +61,14 @@ def preproc(ctx: PreProcContext):
|
|
|
61
61
|
"repo_roles": "repo_roles",
|
|
62
62
|
"users": "users",
|
|
63
63
|
"teams": "teams",
|
|
64
|
+
"team_external_groups": "team_external_groups",
|
|
64
65
|
"team_members": "team_members",
|
|
65
66
|
"saml_provider": "saml_provider",
|
|
66
67
|
"external_identities": "external_identities",
|
|
67
68
|
"applications": "applications",
|
|
68
69
|
"enterprise": "enterprise",
|
|
69
70
|
"enterprise_organizations": "enterprise_organizations",
|
|
71
|
+
"enterprise_scim_groups": "enterprise_scim_groups",
|
|
70
72
|
"enterprise_runner_groups": "enterprise_runner_groups",
|
|
71
73
|
"enterprise_runner_group_organizations": "enterprise_runner_group_organizations",
|
|
72
74
|
"enterprise_runner_group_memberships": "enterprise_runner_group_memberships",
|
|
@@ -62,7 +62,7 @@ from .scim_user import (
|
|
|
62
62
|
ScimUser,
|
|
63
63
|
)
|
|
64
64
|
from .secret_scanning_alert import SecretScanningAlert
|
|
65
|
-
from .team import Team
|
|
65
|
+
from .team import Team, TeamExternalGroup
|
|
66
66
|
from .team_member import TeamMember
|
|
67
67
|
from .team_role import TeamRole
|
|
68
68
|
from .user import User
|
|
@@ -78,6 +78,7 @@ __all__ = [
|
|
|
78
78
|
"ActionPermission",
|
|
79
79
|
"User",
|
|
80
80
|
"Team",
|
|
81
|
+
"TeamExternalGroup",
|
|
81
82
|
"TeamRole",
|
|
82
83
|
"TeamMember",
|
|
83
84
|
"Repository",
|
|
@@ -15,7 +15,7 @@ from openhound_github.main import app
|
|
|
15
15
|
@dataclass
|
|
16
16
|
class GHTeamProperties(GHNodeProperties):
|
|
17
17
|
"""Team-specific properties and accordion panel queries.
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
Attributes:
|
|
20
20
|
collected: Collected/generated by OpenHound
|
|
21
21
|
github_team_id: The raw GitHub team node ID.
|
|
@@ -23,6 +23,8 @@ class GHTeamProperties(GHNodeProperties):
|
|
|
23
23
|
description: The team's description.
|
|
24
24
|
privacy: The team's privacy level (e.g., `visible`, `secret`).
|
|
25
25
|
type: The team type, such as `enterprise` for projected enterprise teams.
|
|
26
|
+
external_group_id: GitHub's org-scoped external IdP group identifier.
|
|
27
|
+
external_group_name: The external IdP group name reported by GitHub.
|
|
26
28
|
environment_name: The name of the environment (GitHub organization).
|
|
27
29
|
query_first_degree_members: Query for first degree members.
|
|
28
30
|
query_unrolled_members: Query for unrolled members.
|
|
@@ -40,6 +42,8 @@ class GHTeamProperties(GHNodeProperties):
|
|
|
40
42
|
description: str | None = None
|
|
41
43
|
privacy: str | None = None
|
|
42
44
|
type: str | None = None
|
|
45
|
+
external_group_id: int | None = None
|
|
46
|
+
external_group_name: str | None = None
|
|
43
47
|
environment_name: str | None = None
|
|
44
48
|
|
|
45
49
|
query_first_degree_members: str | None = None
|
|
@@ -74,6 +78,16 @@ class ParentTeam(BaseModel):
|
|
|
74
78
|
id: str
|
|
75
79
|
|
|
76
80
|
|
|
81
|
+
class TeamExternalGroup(BaseModel):
|
|
82
|
+
"""Raw mapping between an org team and its linked external IdP group."""
|
|
83
|
+
|
|
84
|
+
org_login: str
|
|
85
|
+
team_database_id: int
|
|
86
|
+
external_group_id: int
|
|
87
|
+
external_group_name: str
|
|
88
|
+
external_group_updated_at: str | None = None
|
|
89
|
+
|
|
90
|
+
|
|
77
91
|
@app.asset(
|
|
78
92
|
node=NodeDef(
|
|
79
93
|
kind=nk.TEAM,
|
|
@@ -89,6 +103,13 @@ class ParentTeam(BaseModel):
|
|
|
89
103
|
description="Team is a child of parent team",
|
|
90
104
|
traversable=False,
|
|
91
105
|
),
|
|
106
|
+
EdgeDef(
|
|
107
|
+
start=nk.SCIM_GROUP,
|
|
108
|
+
end=nk.TEAM,
|
|
109
|
+
kind=ek.SCIM_PROVISIONED,
|
|
110
|
+
description="SCIM group is provisioned as team",
|
|
111
|
+
traversable=True,
|
|
112
|
+
),
|
|
92
113
|
],
|
|
93
114
|
)
|
|
94
115
|
class Team(BaseAsset):
|
|
@@ -120,9 +141,16 @@ class Team(BaseAsset):
|
|
|
120
141
|
"""The ID from a GraphQL API response is the same as a regular node_id"""
|
|
121
142
|
return self.id
|
|
122
143
|
|
|
144
|
+
@property
|
|
145
|
+
def external_group(self) -> tuple[int, str] | None:
|
|
146
|
+
if self.database_id is None:
|
|
147
|
+
return None
|
|
148
|
+
return self._lookup.external_group_for_team(self.org_login, self.database_id)
|
|
149
|
+
|
|
123
150
|
@property
|
|
124
151
|
def as_node(self) -> GHNode:
|
|
125
152
|
tid = self.node_id
|
|
153
|
+
external_group = self.external_group
|
|
126
154
|
return GHNode(
|
|
127
155
|
kinds=[nk.TEAM],
|
|
128
156
|
properties=GHTeamProperties(
|
|
@@ -132,6 +160,8 @@ class Team(BaseAsset):
|
|
|
132
160
|
slug=self.slug,
|
|
133
161
|
description=self.description,
|
|
134
162
|
privacy=self.privacy,
|
|
163
|
+
external_group_id=external_group[0] if external_group else None,
|
|
164
|
+
external_group_name=external_group[1] if external_group else None,
|
|
135
165
|
environment_name=self.org_login,
|
|
136
166
|
environmentid=self.org_node_id,
|
|
137
167
|
query_first_degree_members=f"MATCH p=(:GH_User)-[:GH_HasRole]->(t:GH_TeamRole)-[:GH_MemberOf]->(:GH_Team {{node_id:'{tid}'}}) RETURN p",
|
|
@@ -176,6 +206,19 @@ class Team(BaseAsset):
|
|
|
176
206
|
properties=EdgeProperties(traversable=True),
|
|
177
207
|
)
|
|
178
208
|
|
|
209
|
+
external_group = self.external_group
|
|
210
|
+
if external_group:
|
|
211
|
+
scim_group_id = self._lookup.scim_group_id_for_team_external_group(
|
|
212
|
+
self.org_login, external_group[1]
|
|
213
|
+
)
|
|
214
|
+
if scim_group_id:
|
|
215
|
+
yield Edge(
|
|
216
|
+
kind=ek.SCIM_PROVISIONED,
|
|
217
|
+
start=EdgePath(value=scim_group_id, match_by="id"),
|
|
218
|
+
end=EdgePath(value=self.node_id, match_by="id"),
|
|
219
|
+
properties=EdgeProperties(traversable=True),
|
|
220
|
+
)
|
|
221
|
+
|
|
179
222
|
# yield from self._branch_edges
|
|
180
223
|
yield from self._bypass_pull_request_allowances_edges
|
|
181
224
|
yield from self._bypass_push_allowances_edges
|
{openhound_github-0.6.7 → openhound_github-0.7.0}/src/openhound_github/resources/organization.py
RENAMED
|
@@ -74,6 +74,7 @@ from openhound_github.models import (
|
|
|
74
74
|
SelectedOrgSecret,
|
|
75
75
|
SelectedOrgVariable,
|
|
76
76
|
Team,
|
|
77
|
+
TeamExternalGroup,
|
|
77
78
|
TeamMember,
|
|
78
79
|
TeamRole,
|
|
79
80
|
User,
|
|
@@ -111,6 +112,7 @@ class SourceContext:
|
|
|
111
112
|
cache_lock: Lock = field(default_factory=Lock)
|
|
112
113
|
organizations_cache: dict[str, dict[str, Any]] = field(default_factory=dict)
|
|
113
114
|
app_cache: dict[str, dict[str, Any]] = field(default_factory=dict)
|
|
115
|
+
team_rest_cache: dict[str, list[dict[str, Any]]] = field(default_factory=dict)
|
|
114
116
|
actions_permissions_cache: dict[str, dict[str, Any]] = field(default_factory=dict)
|
|
115
117
|
runner_permissions_cache: dict[str, dict[str, Any]] = field(default_factory=dict)
|
|
116
118
|
workflow_permissions_cache: dict[str, dict[str, Any]] = field(default_factory=dict)
|
|
@@ -222,6 +224,183 @@ def _workflow_permissions(
|
|
|
222
224
|
)
|
|
223
225
|
|
|
224
226
|
|
|
227
|
+
def _rest_teams_for_org(
|
|
228
|
+
ctx: SourceContext, client: RESTClient, org_name: str
|
|
229
|
+
) -> list[dict[str, Any]]:
|
|
230
|
+
if org_name not in ctx.team_rest_cache:
|
|
231
|
+
with ctx.cache_lock:
|
|
232
|
+
if org_name not in ctx.team_rest_cache:
|
|
233
|
+
ctx.team_rest_cache[org_name] = [
|
|
234
|
+
team
|
|
235
|
+
for page in client.paginate(
|
|
236
|
+
f"/orgs/{org_name}/teams", params={"per_page": 100}
|
|
237
|
+
)
|
|
238
|
+
for team in page
|
|
239
|
+
]
|
|
240
|
+
return ctx.team_rest_cache[org_name]
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _external_group_skip_reason(exception: BaseException) -> str | None:
|
|
244
|
+
if not isinstance(exception, requests.HTTPError) or exception.response is None:
|
|
245
|
+
return None
|
|
246
|
+
|
|
247
|
+
status_code = exception.response.status_code
|
|
248
|
+
if status_code in (401, 403):
|
|
249
|
+
return "the configured credentials do not have Members organization permission at write level"
|
|
250
|
+
if status_code == 404:
|
|
251
|
+
return "the GitHub scope does not expose external groups"
|
|
252
|
+
return None
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _external_group_failure_is_terminal(exception: BaseException) -> bool:
|
|
256
|
+
if not isinstance(exception, requests.HTTPError) or exception.response is None:
|
|
257
|
+
return False
|
|
258
|
+
return exception.response.status_code in (401, 403)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _team_cannot_be_externally_managed(exception: BaseException) -> bool:
|
|
262
|
+
if not isinstance(exception, requests.HTTPError) or exception.response is None:
|
|
263
|
+
return False
|
|
264
|
+
if exception.response.status_code != 400:
|
|
265
|
+
return False
|
|
266
|
+
|
|
267
|
+
try:
|
|
268
|
+
message = str(exception.response.json().get("message", "")).casefold()
|
|
269
|
+
except ValueError:
|
|
270
|
+
return False
|
|
271
|
+
return "cannot be externally managed" in message and "explicit members" in message
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _log_org_external_group_failure(
|
|
275
|
+
resource: str,
|
|
276
|
+
org_name: str,
|
|
277
|
+
exception: BaseException,
|
|
278
|
+
*,
|
|
279
|
+
group_id: int | str | None = None,
|
|
280
|
+
) -> None:
|
|
281
|
+
skip_reason = _external_group_skip_reason(exception)
|
|
282
|
+
if skip_reason:
|
|
283
|
+
logger.warning(
|
|
284
|
+
"Skipping %s for organization '%s': %s",
|
|
285
|
+
resource,
|
|
286
|
+
org_name,
|
|
287
|
+
skip_reason,
|
|
288
|
+
extra={"resource": resource, "phase": "resource_iteration"},
|
|
289
|
+
)
|
|
290
|
+
return
|
|
291
|
+
|
|
292
|
+
group_context = f" external group '{group_id}'" if group_id is not None else ""
|
|
293
|
+
logger.error(
|
|
294
|
+
"Error in resource '%s' processing organization '%s'%s: %s",
|
|
295
|
+
resource,
|
|
296
|
+
org_name,
|
|
297
|
+
group_context,
|
|
298
|
+
exception,
|
|
299
|
+
extra={"resource": resource, "phase": "resource_iteration"},
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _team_database_id(team_id: Any) -> int | None:
|
|
304
|
+
try:
|
|
305
|
+
return int(team_id)
|
|
306
|
+
except (TypeError, ValueError):
|
|
307
|
+
return None
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def _team_external_group_row(
|
|
311
|
+
org_name: str,
|
|
312
|
+
team_id: Any,
|
|
313
|
+
group_id: Any,
|
|
314
|
+
group_name: Any,
|
|
315
|
+
updated_at: Any,
|
|
316
|
+
) -> dict[str, Any] | None:
|
|
317
|
+
team_database_id = _team_database_id(team_id)
|
|
318
|
+
if group_id is None or not group_name:
|
|
319
|
+
return None
|
|
320
|
+
if team_database_id is None:
|
|
321
|
+
return None
|
|
322
|
+
try:
|
|
323
|
+
return {
|
|
324
|
+
"org_login": org_name,
|
|
325
|
+
"team_database_id": team_database_id,
|
|
326
|
+
"external_group_id": int(group_id),
|
|
327
|
+
"external_group_name": str(group_name),
|
|
328
|
+
"external_group_updated_at": updated_at,
|
|
329
|
+
}
|
|
330
|
+
except (TypeError, ValueError):
|
|
331
|
+
return None
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def _team_external_groups_by_group(
|
|
335
|
+
client: RESTClient,
|
|
336
|
+
org_name: str,
|
|
337
|
+
groups: list[dict[str, Any]],
|
|
338
|
+
allowed_team_database_ids: set[int],
|
|
339
|
+
) -> Iterator[dict[str, Any]]:
|
|
340
|
+
for group in groups:
|
|
341
|
+
group_id = group.get("group_id")
|
|
342
|
+
try:
|
|
343
|
+
response = client.get(f"/orgs/{org_name}/external-group/{group_id}")
|
|
344
|
+
response.raise_for_status()
|
|
345
|
+
group_details = response.json()
|
|
346
|
+
except Exception as e:
|
|
347
|
+
_log_org_external_group_failure(
|
|
348
|
+
"team_external_groups",
|
|
349
|
+
org_name,
|
|
350
|
+
e,
|
|
351
|
+
group_id=group_id,
|
|
352
|
+
)
|
|
353
|
+
if _external_group_failure_is_terminal(e):
|
|
354
|
+
return
|
|
355
|
+
continue
|
|
356
|
+
|
|
357
|
+
group_name = group_details.get("group_name") or group.get("group_name")
|
|
358
|
+
updated_at = group_details.get("updated_at") or group.get("updated_at")
|
|
359
|
+
for team in group_details.get("teams") or []:
|
|
360
|
+
team_database_id = _team_database_id(team.get("team_id"))
|
|
361
|
+
if team_database_id not in allowed_team_database_ids:
|
|
362
|
+
continue
|
|
363
|
+
row = _team_external_group_row(
|
|
364
|
+
org_name,
|
|
365
|
+
team_database_id,
|
|
366
|
+
group_id,
|
|
367
|
+
group_name,
|
|
368
|
+
updated_at,
|
|
369
|
+
)
|
|
370
|
+
if row:
|
|
371
|
+
yield row
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def _team_external_groups_by_team(
|
|
375
|
+
client: RESTClient, org_name: str, teams: list[dict[str, Any]]
|
|
376
|
+
) -> Iterator[dict[str, Any]]:
|
|
377
|
+
for team in teams:
|
|
378
|
+
try:
|
|
379
|
+
response = client.get(
|
|
380
|
+
f"/orgs/{org_name}/teams/{_encode_path_segment(str(team['slug']))}/external-groups"
|
|
381
|
+
)
|
|
382
|
+
response.raise_for_status()
|
|
383
|
+
group_details = response.json()
|
|
384
|
+
except Exception as e:
|
|
385
|
+
if _team_cannot_be_externally_managed(e):
|
|
386
|
+
continue
|
|
387
|
+
_log_org_external_group_failure("team_external_groups", org_name, e)
|
|
388
|
+
if _external_group_failure_is_terminal(e):
|
|
389
|
+
return
|
|
390
|
+
continue
|
|
391
|
+
|
|
392
|
+
for group in group_details.get("groups") or []:
|
|
393
|
+
row = _team_external_group_row(
|
|
394
|
+
org_name,
|
|
395
|
+
team.get("id"),
|
|
396
|
+
group.get("group_id"),
|
|
397
|
+
group.get("group_name"),
|
|
398
|
+
group.get("updated_at"),
|
|
399
|
+
)
|
|
400
|
+
if row:
|
|
401
|
+
yield row
|
|
402
|
+
|
|
403
|
+
|
|
225
404
|
def _repo_permission_role(repo: dict[str, Any]) -> str:
|
|
226
405
|
role_name = repo.get("role_name")
|
|
227
406
|
if role_name:
|
|
@@ -574,12 +753,9 @@ def projected_enterprise_teams(ctx: SourceContext):
|
|
|
574
753
|
org_name = org.org_name
|
|
575
754
|
client = org.client
|
|
576
755
|
try:
|
|
577
|
-
for
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
for team in page:
|
|
581
|
-
if str(team.get("slug", "")).startswith("ent:") and team.get("node_id"):
|
|
582
|
-
yield {**team, "org_login": org_name}
|
|
756
|
+
for team in _rest_teams_for_org(ctx, client, org_name):
|
|
757
|
+
if str(team.get("slug", "")).startswith("ent:") and team.get("node_id"):
|
|
758
|
+
yield {**team, "org_login": org_name}
|
|
583
759
|
except Exception as e:
|
|
584
760
|
logger.error(
|
|
585
761
|
f"Error in resource 'projected_enterprise_teams' processing organization '{org_name}': {e}",
|
|
@@ -588,6 +764,58 @@ def projected_enterprise_teams(ctx: SourceContext):
|
|
|
588
764
|
continue
|
|
589
765
|
|
|
590
766
|
|
|
767
|
+
@app.resource(
|
|
768
|
+
name="team_external_groups",
|
|
769
|
+
columns=TeamExternalGroup,
|
|
770
|
+
parallelized=True,
|
|
771
|
+
)
|
|
772
|
+
def team_external_groups(ctx: SourceContext):
|
|
773
|
+
"""Collect external IdP group mappings for normal organization teams."""
|
|
774
|
+
|
|
775
|
+
for org in ctx.organizations:
|
|
776
|
+
org_name = org.org_name
|
|
777
|
+
client = org.client
|
|
778
|
+
try:
|
|
779
|
+
groups = [
|
|
780
|
+
group
|
|
781
|
+
for page in client.paginate(
|
|
782
|
+
f"/orgs/{org_name}/external-groups",
|
|
783
|
+
params={"per_page": 100},
|
|
784
|
+
data_selector="groups",
|
|
785
|
+
)
|
|
786
|
+
for group in page
|
|
787
|
+
if group.get("group_id") is not None
|
|
788
|
+
]
|
|
789
|
+
if not groups:
|
|
790
|
+
continue
|
|
791
|
+
|
|
792
|
+
teams = [
|
|
793
|
+
team
|
|
794
|
+
for team in _rest_teams_for_org(ctx, client, org_name)
|
|
795
|
+
if team.get("id") is not None
|
|
796
|
+
and team.get("slug")
|
|
797
|
+
and not str(team["slug"]).startswith("ent:")
|
|
798
|
+
]
|
|
799
|
+
team_database_ids = {
|
|
800
|
+
team_database_id
|
|
801
|
+
for team in teams
|
|
802
|
+
if (team_database_id := _team_database_id(team["id"])) is not None
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
if len(groups) <= len(teams):
|
|
806
|
+
yield from _team_external_groups_by_group(
|
|
807
|
+
client,
|
|
808
|
+
org_name,
|
|
809
|
+
groups,
|
|
810
|
+
team_database_ids,
|
|
811
|
+
)
|
|
812
|
+
else:
|
|
813
|
+
yield from _team_external_groups_by_team(client, org_name, teams)
|
|
814
|
+
except Exception as e:
|
|
815
|
+
_log_org_external_group_failure("team_external_groups", org_name, e)
|
|
816
|
+
continue
|
|
817
|
+
|
|
818
|
+
|
|
591
819
|
@app.transformer(name="team_roles", columns=TeamRole, parallelized=True)
|
|
592
820
|
def team_roles(team: Team):
|
|
593
821
|
"""Yield the two built-in team roles (members and maintainers) for a team.
|
|
@@ -2061,6 +2289,7 @@ def organization_resources(ctx: SourceContext):
|
|
|
2061
2289
|
personal_access_tokens_resource = personal_access_tokens(ctx)
|
|
2062
2290
|
|
|
2063
2291
|
teams_resource = teams(ctx)
|
|
2292
|
+
team_external_groups_resource = team_external_groups(ctx)
|
|
2064
2293
|
repositories_graphql_resource = repositories_graphql(ctx)
|
|
2065
2294
|
app_installs_resource = app_installations(ctx)
|
|
2066
2295
|
runner_groups_resource = runner_groups(ctx)
|
|
@@ -2090,6 +2319,7 @@ def organization_resources(ctx: SourceContext):
|
|
|
2090
2319
|
repos_resource | repository_secrets(ctx),
|
|
2091
2320
|
repos_resource | repository_variables(ctx),
|
|
2092
2321
|
teams_resource,
|
|
2322
|
+
team_external_groups_resource,
|
|
2093
2323
|
projected_enterprise_teams_resource,
|
|
2094
2324
|
org_scim_organizations_resource,
|
|
2095
2325
|
teams_resource | team_members(ctx),
|
|
@@ -156,6 +156,7 @@ class SourceContext:
|
|
|
156
156
|
cache_lock: Lock = field(default_factory=Lock)
|
|
157
157
|
organizations_cache: dict[str, dict[str, Any]] = field(default_factory=dict)
|
|
158
158
|
app_cache: dict[str, dict[str, Any]] = field(default_factory=dict)
|
|
159
|
+
team_rest_cache: dict[str, list[dict[str, Any]]] = field(default_factory=dict)
|
|
159
160
|
actions_permissions_cache: dict[str, dict[str, Any]] = field(default_factory=dict)
|
|
160
161
|
runner_permissions_cache: dict[str, dict[str, Any]] = field(default_factory=dict)
|
|
161
162
|
workflow_permissions_cache: dict[str, dict[str, Any]] = field(default_factory=dict)
|