mirror of
https://github.com/BishopFox/cloudfox.git
synced 2026-11-10 17:06:52 -10:00
133 lines
3.3 KiB
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
|
|
}
|
|
|