github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/internal/runners/secrets/sync.go (about)

     1  package secrets
     2  
     3  import (
     4  	"strconv"
     5  
     6  	"github.com/ActiveState/cli/internal/errs"
     7  	"github.com/ActiveState/cli/internal/keypairs"
     8  	"github.com/ActiveState/cli/internal/locale"
     9  	"github.com/ActiveState/cli/internal/logging"
    10  	"github.com/ActiveState/cli/internal/output"
    11  	"github.com/ActiveState/cli/internal/primer"
    12  	"github.com/ActiveState/cli/internal/secrets"
    13  	"github.com/ActiveState/cli/pkg/platform/api"
    14  	"github.com/ActiveState/cli/pkg/platform/api/mono/mono_models"
    15  	secretsapi "github.com/ActiveState/cli/pkg/platform/api/secrets"
    16  	secretsapiClient "github.com/ActiveState/cli/pkg/platform/api/secrets/secrets_client/secrets"
    17  	"github.com/ActiveState/cli/pkg/platform/authentication"
    18  	"github.com/ActiveState/cli/pkg/platform/model"
    19  	"github.com/ActiveState/cli/pkg/project"
    20  )
    21  
    22  type syncPrimeable interface {
    23  	primer.Projecter
    24  	primer.Outputer
    25  	primer.Configurer
    26  	primer.Auther
    27  }
    28  
    29  // Sync manages the synchronization execution context.
    30  type Sync struct {
    31  	secretsClient *secretsapi.Client
    32  	proj          *project.Project
    33  	out           output.Outputer
    34  	cfg           keypairs.Configurable
    35  	auth          *authentication.Auth
    36  }
    37  
    38  // NewSync prepares a sync execution context for use.
    39  func NewSync(client *secretsapi.Client, p syncPrimeable) *Sync {
    40  	return &Sync{
    41  		secretsClient: client,
    42  		proj:          p.Project(),
    43  		out:           p.Output(),
    44  		cfg:           p.Config(),
    45  		auth:          p.Auth(),
    46  	}
    47  }
    48  
    49  // Run executes the sync behavior.
    50  func (s *Sync) Run() error {
    51  	s.out.Notice(locale.Tr("operating_message", s.proj.NamespaceString(), s.proj.Dir()))
    52  	if err := checkSecretsAccess(s.proj, s.auth); err != nil {
    53  		return locale.WrapError(err, "secrets_err_check_access")
    54  	}
    55  
    56  	org, err := model.FetchOrgByURLName(s.proj.Owner(), s.auth)
    57  	if err != nil {
    58  		return locale.WrapError(err, "secrets_err_fetch_org", "Cannot fetch org")
    59  	}
    60  
    61  	updatedCount, err := synchronizeEachOrgMember(s.secretsClient, org, s.cfg, s.auth)
    62  	if err != nil {
    63  		return locale.WrapError(err, "secrets_err_sync", "Cannot synchronize secrets")
    64  	}
    65  
    66  	s.out.Notice(locale.Tr("secrets_sync_results_message", strconv.Itoa(updatedCount), org.DisplayName))
    67  
    68  	return nil
    69  }
    70  
    71  func synchronizeEachOrgMember(secretsClient *secretsapi.Client, org *mono_models.Organization, cfg keypairs.Configurable, auth *authentication.Auth) (count int, f error) {
    72  	sourceKeypair, err := secrets.LoadKeypairFromConfigDir(cfg)
    73  	if err != nil {
    74  		return 0, err
    75  	}
    76  
    77  	members, err := model.FetchOrgMembers(org.URLname, auth)
    78  	if err != nil {
    79  		return 0, err
    80  	}
    81  
    82  	currentUserID, err := secretsClient.AuthenticatedUserID()
    83  	if err != nil {
    84  		return 0, err
    85  	}
    86  
    87  	var updatedCtr int
    88  	for _, member := range members {
    89  		if currentUserID != member.User.UserID {
    90  			params := secretsapiClient.NewDiffUserSecretsParams()
    91  			params.OrganizationID = org.OrganizationID
    92  			params.UserID = member.User.UserID
    93  			diffPayloadOk, err := secretsClient.Secrets.Secrets.DiffUserSecrets(params, auth.ClientAuth())
    94  
    95  			if err != nil {
    96  				switch statusCode := api.ErrorCode(err); statusCode {
    97  				case 404:
    98  					continue // nothing to do when no diff for a user, move on to next one
    99  				case 401:
   100  					return updatedCtr, locale.NewInputError("err_api_not_authenticated")
   101  				default:
   102  					logging.Debug("unknown error diffing user secrets with %s: %v", member.User.UserID.String(), err)
   103  					return updatedCtr, errs.Wrap(err, "Unknown failure")
   104  				}
   105  			}
   106  
   107  			targetShares, err := secrets.ShareFromDiff(sourceKeypair, diffPayloadOk.Payload)
   108  			if err != nil {
   109  				return updatedCtr, err
   110  			}
   111  
   112  			err = secretsapi.SaveSecretShares(secretsClient, org, member.User, targetShares)
   113  			if err != nil {
   114  				return updatedCtr, err
   115  			}
   116  			updatedCtr++
   117  		}
   118  	}
   119  
   120  	return updatedCtr, nil
   121  }