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 }