Skip to content

Commit 26cd345

Browse files
committed
fix: add organization role
1 parent 4e9aca0 commit 26cd345

File tree

10 files changed

+425
-8
lines changed

10 files changed

+425
-8
lines changed

cloudql/github/plugin.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ func Plugin(ctx context.Context) *plugin.Plugin {
6464
"github_artifact_dockerfile": tableGitHubArtifactDockerFile(),
6565
"github_repository_webhook": tableGithubRepositoryWebhook(),
6666
"github_artifact_ai_model": tableGitHubArtifactAIModel(),
67+
"github_organization_role": tableGitHubOrganizationRole(),
6768
},
6869
}
6970
for key, table := range p.TableMap {
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package github
2+
3+
import (
4+
opengovernance "github.com/opengovern/og-describer-github/discovery/pkg/es"
5+
6+
"github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto"
7+
"github.com/turbot/steampipe-plugin-sdk/v5/plugin"
8+
"github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform"
9+
)
10+
11+
func gitHubOrganizationRoleColumns() []*plugin.Column {
12+
tableCols := []*plugin.Column{
13+
{
14+
Name: "id",
15+
Type: proto.ColumnType_STRING,
16+
Description: "The organization the member is associated with.",
17+
Transform: transform.FromField("Description.ID")},
18+
{
19+
Name: "name",
20+
Type: proto.ColumnType_STRING,
21+
Description: "The role this user has in the organization. Returns null if information is not available to viewer.",
22+
Transform: transform.FromField("Description.Name")},
23+
{
24+
Name: "description",
25+
Type: proto.ColumnType_STRING,
26+
Transform: transform.FromField("Description.Description")},
27+
{
28+
Name: "permissions",
29+
Type: proto.ColumnType_JSON,
30+
Description: "permissions",
31+
Transform: transform.FromField("Description.Permissions")},
32+
{
33+
Name: "organization",
34+
Type: proto.ColumnType_JSON,
35+
Description: "permissions",
36+
Transform: transform.FromField("Description.Organization")},
37+
{
38+
Name: "source",
39+
Description: "permissions",
40+
Type: proto.ColumnType_STRING,
41+
Transform: transform.FromField("Description.Source")},
42+
{
43+
Name: "base_role",
44+
Type: proto.ColumnType_STRING,
45+
Description: "permissions",
46+
Transform: transform.FromField("Description.BaseRole")},
47+
{
48+
Name: "created_at",
49+
Type: proto.ColumnType_TIMESTAMP,
50+
Description: "permissions",
51+
Transform: transform.FromField("Description.CreatedAt")},
52+
{
53+
Name: "updated_at",
54+
Type: proto.ColumnType_TIMESTAMP,
55+
Description: "permissions",
56+
Transform: transform.FromField("Description.UpdatedAt")},
57+
}
58+
59+
return append(tableCols, sharedUserColumns()...)
60+
}
61+
62+
func tableGitHubOrganizationRole() *plugin.Table {
63+
return &plugin.Table{
64+
Name: "github_organization_role",
65+
Description: "GitHub roles for a given organization. GitHub Users are user accounts in GitHub.",
66+
List: &plugin.ListConfig{
67+
Hydrate: opengovernance.ListOrganizationRole,
68+
},
69+
Columns: commonColumns(gitHubOrganizationRoleColumns()),
70+
}
71+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package describers
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"github.com/opengovern/og-describer-github/discovery/pkg/models"
8+
model "github.com/opengovern/og-describer-github/discovery/provider"
9+
resilientbridge "github.com/opengovern/resilient-bridge"
10+
"github.com/opengovern/resilient-bridge/adapters"
11+
"strconv"
12+
"time"
13+
)
14+
15+
func ListOrganizationRoles(ctx context.Context,
16+
githubClient model.GitHubClient,
17+
organizationName string,
18+
stream *models.StreamSender) ([]models.Resource, error) {
19+
sdk := resilientbridge.NewResilientBridge()
20+
sdk.RegisterProvider("github", adapters.NewGitHubAdapter(githubClient.Token), &resilientbridge.ProviderConfig{
21+
UseProviderLimits: true,
22+
MaxRetries: 3,
23+
BaseBackoff: 0,
24+
})
25+
26+
var values []models.Resource
27+
28+
endpoint := fmt.Sprintf("/orgs/%s/organization-roles", organizationName)
29+
req := &resilientbridge.NormalizedRequest{
30+
Method: "GET",
31+
Endpoint: endpoint,
32+
Headers: map[string]string{"Accept": "application/vnd.github+json"},
33+
}
34+
35+
resp, err := sdk.Request("github", req)
36+
if err != nil {
37+
return nil, fmt.Errorf("error fetching repos: %w", err)
38+
}
39+
if resp.StatusCode != 200 {
40+
return nil, fmt.Errorf("HTTP error %d: %s", resp.StatusCode, string(resp.Data))
41+
}
42+
43+
// Decode into a slice of generic maps. We'll only parse name, archived, disabled, etc.
44+
var rolesResponse OrganizationRolesResponse
45+
if err := json.Unmarshal(resp.Data, &rolesResponse); err != nil {
46+
return nil, fmt.Errorf("error decoding repos list: %w", err)
47+
}
48+
49+
for _, r := range rolesResponse.Roles {
50+
value := models.Resource{
51+
ID: strconv.Itoa(r.ID),
52+
Name: r.Name,
53+
Description: model.OrganizationRoleDescription{
54+
Organization: r.Organization,
55+
Name: r.Name,
56+
ID: r.ID,
57+
Source: r.Source,
58+
BaseRole: r.BaseRole,
59+
Permissions: r.Permissions,
60+
Description: r.Description,
61+
CreatedAt: r.CreatedAt,
62+
UpdatedAt: r.UpdatedAt,
63+
},
64+
}
65+
if stream != nil {
66+
if err := (*stream)(value); err != nil {
67+
return nil, err
68+
}
69+
} else {
70+
values = append(values, value)
71+
}
72+
}
73+
74+
return values, nil
75+
}
76+
77+
type OrganizationRole struct {
78+
ID int `json:"id"`
79+
Name string `json:"name"`
80+
Description string `json:"description"`
81+
Organization interface{} `json:"organization"`
82+
Permissions []string `json:"permissions"`
83+
BaseRole string `json:"base_role"`
84+
Source string `json:"source"`
85+
CreatedAt time.Time `json:"created_at"`
86+
UpdatedAt time.Time `json:"updated_at"`
87+
}
88+
89+
type OrganizationRolesResponse struct {
90+
Roles []OrganizationRole `json:"roles"`
91+
}

discovery/pkg/es/resources_clients.go

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8531,3 +8531,212 @@ func GetArtifactAIModel(ctx context.Context, d *plugin.QueryData, _ *plugin.Hydr
85318531
}
85328532

85338533
// ========================== END: ArtifactAIModel =============================
8534+
8535+
// ========================== START: OrganizationRole =============================
8536+
8537+
type OrganizationRole struct {
8538+
ResourceID string `json:"resource_id"`
8539+
PlatformID string `json:"platform_id"`
8540+
Description github.OrganizationRoleDescription `json:"Description"`
8541+
Metadata github.Metadata `json:"metadata"`
8542+
DescribedBy string `json:"described_by"`
8543+
ResourceType string `json:"resource_type"`
8544+
IntegrationType string `json:"integration_type"`
8545+
IntegrationID string `json:"integration_id"`
8546+
}
8547+
8548+
type OrganizationRoleHit struct {
8549+
ID string `json:"_id"`
8550+
Score float64 `json:"_score"`
8551+
Index string `json:"_index"`
8552+
Type string `json:"_type"`
8553+
Version int64 `json:"_version,omitempty"`
8554+
Source OrganizationRole `json:"_source"`
8555+
Sort []interface{} `json:"sort"`
8556+
}
8557+
8558+
type OrganizationRoleHits struct {
8559+
Total essdk.SearchTotal `json:"total"`
8560+
Hits []OrganizationRoleHit `json:"hits"`
8561+
}
8562+
8563+
type OrganizationRoleSearchResponse struct {
8564+
PitID string `json:"pit_id"`
8565+
Hits OrganizationRoleHits `json:"hits"`
8566+
}
8567+
8568+
type OrganizationRolePaginator struct {
8569+
paginator *essdk.BaseESPaginator
8570+
}
8571+
8572+
func (k Client) NewOrganizationRolePaginator(filters []essdk.BoolFilter, limit *int64) (OrganizationRolePaginator, error) {
8573+
paginator, err := essdk.NewPaginator(k.ES(), "github_organization_role", filters, limit)
8574+
if err != nil {
8575+
return OrganizationRolePaginator{}, err
8576+
}
8577+
8578+
p := OrganizationRolePaginator{
8579+
paginator: paginator,
8580+
}
8581+
8582+
return p, nil
8583+
}
8584+
8585+
func (p OrganizationRolePaginator) HasNext() bool {
8586+
return !p.paginator.Done()
8587+
}
8588+
8589+
func (p OrganizationRolePaginator) Close(ctx context.Context) error {
8590+
return p.paginator.Deallocate(ctx)
8591+
}
8592+
8593+
func (p OrganizationRolePaginator) NextPage(ctx context.Context) ([]OrganizationRole, error) {
8594+
var response OrganizationRoleSearchResponse
8595+
err := p.paginator.Search(ctx, &response)
8596+
if err != nil {
8597+
return nil, err
8598+
}
8599+
8600+
var values []OrganizationRole
8601+
for _, hit := range response.Hits.Hits {
8602+
values = append(values, hit.Source)
8603+
}
8604+
8605+
hits := int64(len(response.Hits.Hits))
8606+
if hits > 0 {
8607+
p.paginator.UpdateState(hits, response.Hits.Hits[hits-1].Sort, response.PitID)
8608+
} else {
8609+
p.paginator.UpdateState(hits, nil, "")
8610+
}
8611+
8612+
return values, nil
8613+
}
8614+
8615+
var listOrganizationRoleFilters = map[string]string{
8616+
"has_two_factor_enabled": "Description.HasTwoFactorEnabled",
8617+
"login_id": "Description.LoginID",
8618+
"organization": "Description.Organization",
8619+
"role": "Description.Role",
8620+
}
8621+
8622+
func ListOrganizationRole(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) {
8623+
plugin.Logger(ctx).Trace("ListOrganizationRole")
8624+
runtime.GC()
8625+
8626+
// create service
8627+
cfg := essdk.GetConfig(d.Connection)
8628+
ke, err := essdk.NewClientCached(cfg, d.ConnectionCache, ctx)
8629+
if err != nil {
8630+
plugin.Logger(ctx).Error("ListOrganizationRole NewClientCached", "error", err)
8631+
return nil, err
8632+
}
8633+
k := Client{Client: ke}
8634+
8635+
sc, err := steampipesdk.NewSelfClientCached(ctx, d.ConnectionCache)
8636+
if err != nil {
8637+
plugin.Logger(ctx).Error("ListOrganizationRole NewSelfClientCached", "error", err)
8638+
return nil, err
8639+
}
8640+
integrationId, err := sc.GetConfigTableValueOrNil(ctx, steampipesdk.OpenGovernanceConfigKeyIntegrationID)
8641+
if err != nil {
8642+
plugin.Logger(ctx).Error("ListOrganizationRole GetConfigTableValueOrNil for OpenGovernanceConfigKeyIntegrationID", "error", err)
8643+
return nil, err
8644+
}
8645+
encodedResourceCollectionFilters, err := sc.GetConfigTableValueOrNil(ctx, steampipesdk.OpenGovernanceConfigKeyResourceCollectionFilters)
8646+
if err != nil {
8647+
plugin.Logger(ctx).Error("ListOrganizationRole GetConfigTableValueOrNil for OpenGovernanceConfigKeyResourceCollectionFilters", "error", err)
8648+
return nil, err
8649+
}
8650+
clientType, err := sc.GetConfigTableValueOrNil(ctx, steampipesdk.OpenGovernanceConfigKeyClientType)
8651+
if err != nil {
8652+
plugin.Logger(ctx).Error("ListOrganizationRole GetConfigTableValueOrNil for OpenGovernanceConfigKeyClientType", "error", err)
8653+
return nil, err
8654+
}
8655+
8656+
paginator, err := k.NewOrganizationRolePaginator(essdk.BuildFilter(ctx, d.QueryContext, listOrganizationRoleFilters, integrationId, encodedResourceCollectionFilters, clientType), d.QueryContext.Limit)
8657+
if err != nil {
8658+
plugin.Logger(ctx).Error("ListOrganizationRole NewOrganizationRolePaginator", "error", err)
8659+
return nil, err
8660+
}
8661+
8662+
for paginator.HasNext() {
8663+
page, err := paginator.NextPage(ctx)
8664+
if err != nil {
8665+
plugin.Logger(ctx).Error("ListOrganizationRole paginator.NextPage", "error", err)
8666+
return nil, err
8667+
}
8668+
8669+
for _, v := range page {
8670+
d.StreamListItem(ctx, v)
8671+
}
8672+
}
8673+
8674+
err = paginator.Close(ctx)
8675+
if err != nil {
8676+
return nil, err
8677+
}
8678+
8679+
return nil, nil
8680+
}
8681+
8682+
var getOrganizationRoleFilters = map[string]string{
8683+
"has_two_factor_enabled": "Description.HasTwoFactorEnabled",
8684+
"login_id": "Description.LoginID",
8685+
"organization": "Description.Organization",
8686+
"role": "Description.Role",
8687+
}
8688+
8689+
func GetOrganizationRole(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) {
8690+
plugin.Logger(ctx).Trace("GetOrganizationRole")
8691+
runtime.GC()
8692+
// create service
8693+
cfg := essdk.GetConfig(d.Connection)
8694+
ke, err := essdk.NewClientCached(cfg, d.ConnectionCache, ctx)
8695+
if err != nil {
8696+
return nil, err
8697+
}
8698+
k := Client{Client: ke}
8699+
8700+
sc, err := steampipesdk.NewSelfClientCached(ctx, d.ConnectionCache)
8701+
if err != nil {
8702+
return nil, err
8703+
}
8704+
integrationId, err := sc.GetConfigTableValueOrNil(ctx, steampipesdk.OpenGovernanceConfigKeyIntegrationID)
8705+
if err != nil {
8706+
return nil, err
8707+
}
8708+
encodedResourceCollectionFilters, err := sc.GetConfigTableValueOrNil(ctx, steampipesdk.OpenGovernanceConfigKeyResourceCollectionFilters)
8709+
if err != nil {
8710+
return nil, err
8711+
}
8712+
clientType, err := sc.GetConfigTableValueOrNil(ctx, steampipesdk.OpenGovernanceConfigKeyClientType)
8713+
if err != nil {
8714+
return nil, err
8715+
}
8716+
8717+
limit := int64(1)
8718+
paginator, err := k.NewOrganizationRolePaginator(essdk.BuildFilter(ctx, d.QueryContext, getOrganizationRoleFilters, integrationId, encodedResourceCollectionFilters, clientType), &limit)
8719+
if err != nil {
8720+
return nil, err
8721+
}
8722+
8723+
for paginator.HasNext() {
8724+
page, err := paginator.NextPage(ctx)
8725+
if err != nil {
8726+
return nil, err
8727+
}
8728+
8729+
for _, v := range page {
8730+
return v, nil
8731+
}
8732+
}
8733+
8734+
err = paginator.Close(ctx)
8735+
if err != nil {
8736+
return nil, err
8737+
}
8738+
8739+
return nil, nil
8740+
}
8741+
8742+
// ========================== END: OrganizationRole =============================

0 commit comments

Comments
 (0)