From a2b6355fb593aef968599964d1dc3ffe15aa1ec8 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Tue, 28 Jun 2016 17:33:39 +0800 Subject: [PATCH 1/6] audit deleting repository --- api/repository.go | 10 ---- service/notification.go | 107 +++++++++++++++++++++++++++------------- 2 files changed, 72 insertions(+), 45 deletions(-) diff --git a/api/repository.go b/api/repository.go index b4b36c090b..57564e61fa 100644 --- a/api/repository.go +++ b/api/repository.go @@ -154,17 +154,7 @@ func (ra *RepositoryAPI) Delete() { ra.CustomAbort(http.StatusInternalServerError, "internal error") } log.Infof("delete tag: %s %s", repoName, t) - go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete) - } - - go func() { - log.Debug("refreshing catalog cache") - if err := cache.RefreshCatalogCache(); err != nil { - log.Errorf("error occurred while refresh catalog cache: %v", err) - } - }() - } type tag struct { diff --git a/service/notification.go b/service/notification.go index 2bd3919249..e72b134198 100644 --- a/service/notification.go +++ b/service/notification.go @@ -39,55 +39,92 @@ const manifestPattern = `^application/vnd.docker.distribution.manifest.v\d\+json // Post handles POST request, and records audit log or refreshes cache based on event. func (n *NotificationHandler) Post() { var notification models.Notification - //log.Info("Notification Handler triggered!\n") - // log.Infof("request body in string: %s", string(n.Ctx.Input.CopyBody())) + log.Infof("request body in string: %s", string(n.Ctx.Input.CopyBody(1<<32))) err := json.Unmarshal(n.Ctx.Input.CopyBody(1<<32), ¬ification) if err != nil { - log.Errorf("error while decoding json: %v", err) + log.Errorf("failed to decode notification: %v", err) return } - var username, action, repo, project, repoTag string - var matched bool - for _, e := range notification.Events { - matched, err = regexp.MatchString(manifestPattern, e.Target.MediaType) - if err != nil { - log.Errorf("Failed to match the media type against pattern, error: %v", err) - matched = false + + events, err := filterEvents(¬ification) + if err != nil { + log.Errorf("failed to filter events: %v", err) + return + } + + for _, event := range events { + repository := event.Target.Repository + + project := "" + if strings.Contains(repository, "/") { + project = repository[0:strings.LastIndex(repository, "/")] } - if matched && (strings.HasPrefix(e.Request.UserAgent, "docker") || - strings.ToLower(strings.TrimSpace(e.Request.UserAgent)) == "harbor-registry-client") { - username = e.Actor.Name - action = e.Action - repo = e.Target.Repository - repoTag = e.Target.Tag - log.Debugf("repo tag is : %v ", repoTag) - if strings.Contains(repo, "/") { - project = repo[0:strings.LastIndex(repo, "/")] - } - if username == "" { - username = "anonymous" - } + tag := event.Target.Tag + action := event.Action - if action == "pull" && username == "job-service-user" { - return - } + user := event.Actor.Name + if len(user) == 0 { + user = "anonymous" + } - go dao.AccessLog(username, project, repo, repoTag, action) + go dao.AccessLog(user, project, repository, tag, action) + if action == "push" || action == "delete" { + go func() { + if err := cache.RefreshCatalogCache(); err != nil { + log.Errorf("failed to refresh cache: %v", err) + } + }() + + operation := "" if action == "push" { - go func() { - err2 := cache.RefreshCatalogCache() - if err2 != nil { - log.Errorf("Error happens when refreshing cache: %v", err2) - } - }() - - go api.TriggerReplicationByRepository(repo, []string{repoTag}, models.RepOpTransfer) + operation = models.RepOpTransfer + } else { + operation = models.RepOpDelete } + + go api.TriggerReplicationByRepository(repository, []string{tag}, operation) + } + } +} + +func filterEvents(notification *models.Notification) ([]*models.Event, error) { + events := []*models.Event{} + + for _, event := range notification.Events { + + //delete + // TODO add tag field + if event.Action == "delete" { + events = append(events, &event) + continue + } + + isManifest, err := regexp.MatchString(manifestPattern, event.Target.MediaType) + if err != nil { + log.Errorf("failed to match the media type against pattern: %v", err) + continue + } + + if !isManifest { + continue + } + + //pull and push manifest by docker-client + if strings.HasPrefix(event.Request.UserAgent, "docker") && (event.Action == "pull" || event.Action == "push") { + events = append(events, &event) + continue + } + + //push manifest by docker-client or job-service + if strings.ToLower(strings.TrimSpace(event.Request.UserAgent)) == "harbor-registry-client" && event.Action == "push" { + events = append(events, &event) + continue } } + return events, nil } // Render returns nil as it won't render any template. From 10189913127eb8b2f803bfa4518ff707cd4fd108 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Tue, 28 Jun 2016 21:39:38 +0800 Subject: [PATCH 2/6] print log if error occurs --- service/notification.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/service/notification.go b/service/notification.go index e72b134198..ae3747b79e 100644 --- a/service/notification.go +++ b/service/notification.go @@ -69,7 +69,11 @@ func (n *NotificationHandler) Post() { user = "anonymous" } - go dao.AccessLog(user, project, repository, tag, action) + go func() { + if err := dao.AccessLog(user, project, repository, tag, action); err != nil { + log.Errorf("failed to add access log: %v", err) + } + }() if action == "push" || action == "delete" { go func() { if err := cache.RefreshCatalogCache(); err != nil { From ac17546d38f317b8d26666bb7f4b86778fdc54f6 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Tue, 28 Jun 2016 21:47:52 +0800 Subject: [PATCH 3/6] remove useless log --- service/notification.go | 1 - 1 file changed, 1 deletion(-) diff --git a/service/notification.go b/service/notification.go index ae3747b79e..cc1585bc34 100644 --- a/service/notification.go +++ b/service/notification.go @@ -39,7 +39,6 @@ const manifestPattern = `^application/vnd.docker.distribution.manifest.v\d\+json // Post handles POST request, and records audit log or refreshes cache based on event. func (n *NotificationHandler) Post() { var notification models.Notification - log.Infof("request body in string: %s", string(n.Ctx.Input.CopyBody(1<<32))) err := json.Unmarshal(n.Ctx.Input.CopyBody(1<<32), ¬ification) if err != nil { From 23b026655cba0a00ff29273835c8639d405bca2d Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Wed, 29 Jun 2016 17:10:17 +0800 Subject: [PATCH 4/6] handle repository deletion event in API --- api/repository.go | 8 ++++++++ service/notification.go | 12 +----------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/api/repository.go b/api/repository.go index 57564e61fa..f19d99a5e6 100644 --- a/api/repository.go +++ b/api/repository.go @@ -154,7 +154,15 @@ func (ra *RepositoryAPI) Delete() { ra.CustomAbort(http.StatusInternalServerError, "internal error") } log.Infof("delete tag: %s %s", repoName, t) + go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete) } + + go func() { + log.Debug("refreshing catalog cache") + if err := cache.RefreshCatalogCache(); err != nil { + log.Errorf("error occurred while refresh catalog cache: %v", err) + } + }() } type tag struct { diff --git a/service/notification.go b/service/notification.go index cc1585bc34..77ade18d37 100644 --- a/service/notification.go +++ b/service/notification.go @@ -73,7 +73,7 @@ func (n *NotificationHandler) Post() { log.Errorf("failed to add access log: %v", err) } }() - if action == "push" || action == "delete" { + if action == "push" { go func() { if err := cache.RefreshCatalogCache(); err != nil { log.Errorf("failed to refresh cache: %v", err) @@ -83,8 +83,6 @@ func (n *NotificationHandler) Post() { operation := "" if action == "push" { operation = models.RepOpTransfer - } else { - operation = models.RepOpDelete } go api.TriggerReplicationByRepository(repository, []string{tag}, operation) @@ -96,14 +94,6 @@ func filterEvents(notification *models.Notification) ([]*models.Event, error) { events := []*models.Event{} for _, event := range notification.Events { - - //delete - // TODO add tag field - if event.Action == "delete" { - events = append(events, &event) - continue - } - isManifest, err := regexp.MatchString(manifestPattern, event.Target.MediaType) if err != nil { log.Errorf("failed to match the media type against pattern: %v", err) From 2f9ace7eebbb9b7c5d2e9693618e06bcbe87e61c Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Wed, 29 Jun 2016 17:52:35 +0800 Subject: [PATCH 5/6] update --- api/repository.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/api/repository.go b/api/repository.go index f19d99a5e6..5f38832466 100644 --- a/api/repository.go +++ b/api/repository.go @@ -144,6 +144,18 @@ func (ra *RepositoryAPI) Delete() { tags = append(tags, tag) } + project := "" + if strings.Contains(repoName, "/") { + project = repoName[0:strings.LastIndex(repoName, "/")] + } + user, _, ok := ra.Ctx.Request.BasicAuth() + if !ok { + user, err = ra.getUsername() + if err != nil { + log.Errorf("failed to get user: %v", err) + } + } + for _, t := range tags { if err := rc.DeleteTag(t); err != nil { if regErr, ok := err.(*registry_error.Error); ok { @@ -155,6 +167,13 @@ func (ra *RepositoryAPI) Delete() { } log.Infof("delete tag: %s %s", repoName, t) go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete) + + go func() { + + if err := dao.AccessLog(user, project, repoName, t, "delete"); err != nil { + log.Errorf("failed to add access log: %v", err) + } + }() } go func() { From d7f78502d7ef26781dd78afc94334c1f5460e710 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Wed, 29 Jun 2016 18:14:50 +0800 Subject: [PATCH 6/6] pass govet --- api/repository.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/api/repository.go b/api/repository.go index 5f38832466..a303ff11e3 100644 --- a/api/repository.go +++ b/api/repository.go @@ -168,12 +168,11 @@ func (ra *RepositoryAPI) Delete() { log.Infof("delete tag: %s %s", repoName, t) go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete) - go func() { - - if err := dao.AccessLog(user, project, repoName, t, "delete"); err != nil { + go func(tag string) { + if err := dao.AccessLog(user, project, repoName, tag, "delete"); err != nil { log.Errorf("failed to add access log: %v", err) } - }() + }(t) } go func() {