9 "github.com/Sirupsen/logrus"
10 "github.com/docker/notary/client/changelist"
11 store "github.com/docker/notary/storage"
12 "github.com/docker/notary/tuf"
13 "github.com/docker/notary/tuf/data"
14 "github.com/docker/notary/tuf/utils"
17 // Use this to initialize remote HTTPStores from the config settings
18 func getRemoteStore(baseURL, gun string, rt http.RoundTripper) (store.RemoteStore, error) {
19 s, err := store.NewHTTPStore(
20 baseURL+"/v2/"+gun+"/_trust/tuf/",
27 return store.OfflineStore{}, err
32 func applyChangelist(repo *tuf.Repo, invalid *tuf.Repo, cl changelist.Changelist) error {
33 it, err := cl.NewIterator()
43 isDel := data.IsDelegation(c.Scope()) || data.IsWildDelegation(c.Scope())
45 case c.Scope() == changelist.ScopeTargets || isDel:
46 err = applyTargetsChange(repo, invalid, c)
47 case c.Scope() == changelist.ScopeRoot:
48 err = applyRootChange(repo, c)
50 return fmt.Errorf("scope not supported: %s", c.Scope())
53 logrus.Debugf("error attempting to apply change #%d: %s, on scope: %s path: %s type: %s", index, c.Action(), c.Scope(), c.Path(), c.Type())
58 logrus.Debugf("applied %d change(s)", index)
62 func applyTargetsChange(repo *tuf.Repo, invalid *tuf.Repo, c changelist.Change) error {
64 case changelist.TypeTargetsTarget:
65 return changeTargetMeta(repo, c)
66 case changelist.TypeTargetsDelegation:
67 return changeTargetsDelegation(repo, c)
68 case changelist.TypeWitness:
69 return witnessTargets(repo, invalid, c.Scope())
71 return fmt.Errorf("only target meta and delegations changes supported")
75 func changeTargetsDelegation(repo *tuf.Repo, c changelist.Change) error {
77 case changelist.ActionCreate:
78 td := changelist.TUFDelegation{}
79 err := json.Unmarshal(c.Content(), &td)
84 // Try to create brand new role or update one
85 // First add the keys, then the paths. We can only add keys and paths in this scenario
86 err = repo.UpdateDelegationKeys(c.Scope(), td.AddKeys, []string{}, td.NewThreshold)
90 return repo.UpdateDelegationPaths(c.Scope(), td.AddPaths, []string{}, false)
91 case changelist.ActionUpdate:
92 td := changelist.TUFDelegation{}
93 err := json.Unmarshal(c.Content(), &td)
97 if data.IsWildDelegation(c.Scope()) {
98 return repo.PurgeDelegationKeys(c.Scope(), td.RemoveKeys)
101 delgRole, err := repo.GetDelegationRole(c.Scope())
106 // We need to translate the keys from canonical ID to TUF ID for compatibility
107 canonicalToTUFID := make(map[string]string)
108 for tufID, pubKey := range delgRole.Keys {
109 canonicalID, err := utils.CanonicalKeyID(pubKey)
113 canonicalToTUFID[canonicalID] = tufID
116 removeTUFKeyIDs := []string{}
117 for _, canonID := range td.RemoveKeys {
118 removeTUFKeyIDs = append(removeTUFKeyIDs, canonicalToTUFID[canonID])
121 err = repo.UpdateDelegationKeys(c.Scope(), td.AddKeys, removeTUFKeyIDs, td.NewThreshold)
125 return repo.UpdateDelegationPaths(c.Scope(), td.AddPaths, td.RemovePaths, td.ClearAllPaths)
126 case changelist.ActionDelete:
127 return repo.DeleteDelegation(c.Scope())
129 return fmt.Errorf("unsupported action against delegations: %s", c.Action())
134 func changeTargetMeta(repo *tuf.Repo, c changelist.Change) error {
137 case changelist.ActionCreate:
138 logrus.Debug("changelist add: ", c.Path())
139 meta := &data.FileMeta{}
140 err = json.Unmarshal(c.Content(), meta)
144 files := data.Files{c.Path(): *meta}
146 // Attempt to add the target to this role
147 if _, err = repo.AddTargets(c.Scope(), files); err != nil {
148 logrus.Errorf("couldn't add target to %s: %s", c.Scope(), err.Error())
151 case changelist.ActionDelete:
152 logrus.Debug("changelist remove: ", c.Path())
154 // Attempt to remove the target from this role
155 if err = repo.RemoveTargets(c.Scope(), c.Path()); err != nil {
156 logrus.Errorf("couldn't remove target from %s: %s", c.Scope(), err.Error())
160 err = fmt.Errorf("action not yet supported: %s", c.Action())
165 func applyRootChange(repo *tuf.Repo, c changelist.Change) error {
168 case changelist.TypeRootRole:
169 err = applyRootRoleChange(repo, c)
171 err = fmt.Errorf("type of root change not yet supported: %s", c.Type())
173 return err // might be nil
176 func applyRootRoleChange(repo *tuf.Repo, c changelist.Change) error {
178 case changelist.ActionCreate:
179 // replaces all keys for a role
180 d := &changelist.TUFRootData{}
181 err := json.Unmarshal(c.Content(), d)
185 err = repo.ReplaceBaseKeys(d.RoleName, d.Keys...)
190 return fmt.Errorf("action not yet supported for root: %s", c.Action())
195 func nearExpiry(r data.SignedCommon) bool {
196 plus6mo := time.Now().AddDate(0, 6, 0)
197 return r.Expires.Before(plus6mo)
200 func warnRolesNearExpiry(r *tuf.Repo) {
201 //get every role and its respective signed common and call nearExpiry on it
203 if nearExpiry(r.Root.Signed.SignedCommon) {
204 logrus.Warn("root is nearing expiry, you should re-sign the role metadata")
206 //Targets and delegations check
207 for role, signedTOrD := range r.Targets {
208 //signedTOrD is of type *data.SignedTargets
209 if nearExpiry(signedTOrD.Signed.SignedCommon) {
210 logrus.Warn(role, " metadata is nearing expiry, you should re-sign the role metadata")
214 if nearExpiry(r.Snapshot.Signed.SignedCommon) {
215 logrus.Warn("snapshot is nearing expiry, you should re-sign the role metadata")
217 //do not need to worry about Timestamp, notary signer will re-sign with the timestamp key
220 // Fetches a public key from a remote store, given a gun and role
221 func getRemoteKey(url, gun, role string, rt http.RoundTripper) (data.PublicKey, error) {
222 remote, err := getRemoteStore(url, gun, rt)
226 rawPubKey, err := remote.GetKey(role)
231 pubKey, err := data.UnmarshalPublicKey(rawPubKey)
239 // Rotates a private key in a remote store and returns the public key component
240 func rotateRemoteKey(url, gun, role string, rt http.RoundTripper) (data.PublicKey, error) {
241 remote, err := getRemoteStore(url, gun, rt)
245 rawPubKey, err := remote.RotateKey(role)
250 pubKey, err := data.UnmarshalPublicKey(rawPubKey)
258 // signs and serializes the metadata for a canonical role in a TUF repo to JSON
259 func serializeCanonicalRole(tufRepo *tuf.Repo, role string) (out []byte, err error) {
262 case role == data.CanonicalRootRole:
263 s, err = tufRepo.SignRoot(data.DefaultExpires(role))
264 case role == data.CanonicalSnapshotRole:
265 s, err = tufRepo.SignSnapshot(data.DefaultExpires(role))
266 case tufRepo.Targets[role] != nil:
267 s, err = tufRepo.SignTargets(
268 role, data.DefaultExpires(data.CanonicalTargetsRole))
270 err = fmt.Errorf("%s not supported role to sign on the client", role)
277 return json.Marshal(s)