Skip to content

Commit 3e8f858

Browse files
authored
Merge pull request #51 from opengovern/fix-organization
fix: add organization role
2 parents 87dfb77 + 26cd345 commit 3e8f858

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
@@ -8559,3 +8559,212 @@ func GetArtifactAIModel(ctx context.Context, d *plugin.QueryData, _ *plugin.Hydr
85598559
}
85608560

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

0 commit comments

Comments
 (0)