Files
cloudfox/gcp/services/sourceReposService/sourceReposService.go

133 lines
3.3 KiB
Go

package sourcereposservice
import (
"context"
"fmt"
"strings"
gcpinternal "github.com/BishopFox/cloudfox/internal/gcp"
"github.com/BishopFox/cloudfox/internal/gcp/sdk"
sourcerepo "google.golang.org/api/sourcerepo/v1"
)
type SourceReposService struct{
session *gcpinternal.SafeSession
}
func New() *SourceReposService {
return &SourceReposService{}
}
func NewWithSession(session *gcpinternal.SafeSession) *SourceReposService {
return &SourceReposService{
session: session,
}
}
// RepoInfo represents a Cloud Source Repository
type RepoInfo struct {
Name string `json:"name"`
ProjectID string `json:"projectId"`
URL string `json:"url"`
Size int64 `json:"size"`
MirrorConfig bool `json:"mirrorConfig"`
MirrorURL string `json:"mirrorUrl"`
PubsubConfigs int `json:"pubsubConfigs"`
IAMBindings []IAMBinding `json:"iamBindings"`
}
// IAMBinding represents a single IAM binding (one role + one member)
type IAMBinding struct {
Role string `json:"role"`
Member string `json:"member"`
}
// getService returns a source repo service client using cached session if available
func (s *SourceReposService) getService(ctx context.Context) (*sourcerepo.Service, error) {
if s.session != nil {
return sdk.CachedGetSourceRepoService(ctx, s.session)
}
return sourcerepo.NewService(ctx)
}
// ListRepos retrieves all Cloud Source Repositories in a project
func (s *SourceReposService) ListRepos(projectID string) ([]RepoInfo, error) {
ctx := context.Background()
service, err := s.getService(ctx)
if err != nil {
return nil, gcpinternal.ParseGCPError(err, "sourcerepo.googleapis.com")
}
var repos []RepoInfo
parent := fmt.Sprintf("projects/%s", projectID)
resp, err := service.Projects.Repos.List(parent).Context(ctx).Do()
if err != nil {
return nil, gcpinternal.ParseGCPError(err, "sourcerepo.googleapis.com")
}
for _, repo := range resp.Repos {
info := s.parseRepo(repo, projectID)
// Get IAM policy for this repo
iamBindings := s.getRepoIAMBindings(service, repo.Name)
info.IAMBindings = iamBindings
repos = append(repos, info)
}
return repos, nil
}
// getRepoIAMBindings retrieves IAM bindings for a repository
func (s *SourceReposService) getRepoIAMBindings(service *sourcerepo.Service, repoName string) []IAMBinding {
var bindings []IAMBinding
policy, err := service.Projects.Repos.GetIamPolicy(repoName).OptionsRequestedPolicyVersion(3).Do()
if err != nil {
// Silently skip if we can't get IAM policy
return bindings
}
for _, binding := range policy.Bindings {
for _, member := range binding.Members {
bindings = append(bindings, IAMBinding{
Role: binding.Role,
Member: member,
})
}
}
return bindings
}
func (s *SourceReposService) parseRepo(repo *sourcerepo.Repo, projectID string) RepoInfo {
// Extract repo name from full path
name := repo.Name
if strings.Contains(name, "/") {
parts := strings.Split(name, "/")
name = parts[len(parts)-1]
}
info := RepoInfo{
Name: name,
ProjectID: projectID,
URL: repo.Url,
Size: repo.Size,
}
// Check for mirror configuration
if repo.MirrorConfig != nil {
info.MirrorConfig = true
info.MirrorURL = repo.MirrorConfig.Url
}
// Count pubsub configs
if repo.PubsubConfigs != nil {
info.PubsubConfigs = len(repo.PubsubConfigs)
}
return info
}