mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-22 16:48:30 +01:00
drop table access log in migration (#11118)
Use the audit log instead, the access log table should be dropped after migration Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
parent
0422721490
commit
b4e941e961
@ -175,7 +175,6 @@ CREATE TABLE audit_log
|
||||
);
|
||||
|
||||
/*migrate access log to audit log*/
|
||||
/*TODO drop table access_log?*/
|
||||
DO $$
|
||||
DECLARE
|
||||
access RECORD;
|
||||
@ -195,6 +194,9 @@ BEGIN
|
||||
END LOOP;
|
||||
END $$;
|
||||
|
||||
/*drop access table after migrate to audit log*/
|
||||
DROP TABLE IF EXISTS access_log;
|
||||
|
||||
/*remove the constraint for project_id in table 'notification_policy'*/
|
||||
ALTER TABLE notification_policy DROP CONSTRAINT unique_project_id;
|
||||
|
||||
|
@ -1,109 +0,0 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego/orm"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
)
|
||||
|
||||
// AddAccessLog persists the access logs
|
||||
func AddAccessLog(accessLog models.AccessLog) error {
|
||||
// the max length of username in database is 255, replace the last
|
||||
// three charaters with "..." if the length is greater than 256
|
||||
if len(accessLog.Username) > 255 {
|
||||
accessLog.Username = accessLog.Username[:252] + "..."
|
||||
}
|
||||
|
||||
o := GetOrmer()
|
||||
_, err := o.Insert(&accessLog)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetTotalOfAccessLogs ...
|
||||
func GetTotalOfAccessLogs(query *models.LogQueryParam) (int64, error) {
|
||||
return logQueryConditions(query).Count()
|
||||
}
|
||||
|
||||
// GetAccessLogs gets access logs according to different conditions
|
||||
func GetAccessLogs(query *models.LogQueryParam) ([]models.AccessLog, error) {
|
||||
qs := logQueryConditions(query).OrderBy("-op_time")
|
||||
|
||||
if query != nil && query.Pagination != nil {
|
||||
size := query.Pagination.Size
|
||||
if size > 0 {
|
||||
qs = qs.Limit(size)
|
||||
|
||||
page := query.Pagination.Page
|
||||
if page > 0 {
|
||||
qs = qs.Offset((page - 1) * size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logs := []models.AccessLog{}
|
||||
_, err := qs.All(&logs)
|
||||
return logs, err
|
||||
}
|
||||
|
||||
func logQueryConditions(query *models.LogQueryParam) orm.QuerySeter {
|
||||
qs := GetOrmer().QueryTable(&models.AccessLog{})
|
||||
|
||||
if query == nil {
|
||||
return qs
|
||||
}
|
||||
|
||||
if len(query.ProjectIDs) > 0 {
|
||||
qs = qs.Filter("project_id__in", query.ProjectIDs)
|
||||
}
|
||||
if len(query.Username) != 0 {
|
||||
qs = qs.Filter("username__contains", query.Username)
|
||||
}
|
||||
if len(query.Repository) != 0 {
|
||||
qs = qs.Filter("repo_name__contains", query.Repository)
|
||||
}
|
||||
if len(query.Tag) != 0 {
|
||||
qs = qs.Filter("repo_tag__contains", query.Tag)
|
||||
}
|
||||
operations := []string{}
|
||||
for _, operation := range query.Operations {
|
||||
if len(operation) > 0 {
|
||||
operations = append(operations, operation)
|
||||
}
|
||||
}
|
||||
if len(operations) > 0 {
|
||||
qs = qs.Filter("operation__in", operations)
|
||||
}
|
||||
if query.BeginTime != nil {
|
||||
qs = qs.Filter("op_time__gte", query.BeginTime)
|
||||
}
|
||||
if query.EndTime != nil {
|
||||
qs = qs.Filter("op_time__lte", query.EndTime)
|
||||
}
|
||||
|
||||
return qs
|
||||
}
|
||||
|
||||
// CountPull ...
|
||||
func CountPull(repoName string) (int64, error) {
|
||||
o := GetOrmer()
|
||||
num, err := o.QueryTable("access_log").Filter("repo_name", repoName).Filter("operation", "pull").Count()
|
||||
if err != nil {
|
||||
log.Errorf("error in CountPull: %v ", err)
|
||||
return 0, err
|
||||
}
|
||||
return num, nil
|
||||
}
|
@ -71,27 +71,6 @@ func cleanByUser(username string) {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
err = execUpdate(o, `delete
|
||||
from access_log
|
||||
where username = ?
|
||||
`, username)
|
||||
if err != nil {
|
||||
o.Rollback()
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
err = execUpdate(o, `delete
|
||||
from access_log
|
||||
where project_id = (
|
||||
select project_id
|
||||
from project
|
||||
where name = ?
|
||||
)`, projectName)
|
||||
if err != nil {
|
||||
o.Rollback()
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
err = execUpdate(o, `delete from project where name = ?`, projectName)
|
||||
if err != nil {
|
||||
o.Rollback()
|
||||
@ -158,7 +137,7 @@ func testForAll(m *testing.M) int {
|
||||
|
||||
func clearAll() {
|
||||
tables := []string{"project_member",
|
||||
"project_metadata", "access_log", "repository", "replication_policy",
|
||||
"project_metadata", "repository", "replication_policy",
|
||||
"registry", "replication_execution", "replication_task",
|
||||
"replication_schedule_job", "project", "harbor_user"}
|
||||
for _, t := range tables {
|
||||
@ -409,155 +388,6 @@ func TestGetProject(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccessLog(t *testing.T) {
|
||||
|
||||
accessLog := models.AccessLog{
|
||||
Username: currentUser.Username,
|
||||
ProjectID: currentProject.ProjectID,
|
||||
RepoName: currentProject.Name + "/",
|
||||
RepoTag: "N/A",
|
||||
GUID: "N/A",
|
||||
Operation: "create",
|
||||
OpTime: time.Now(),
|
||||
}
|
||||
if err := AddAccessLog(accessLog); err != nil {
|
||||
t.Errorf("failed to add access log: %v", err)
|
||||
}
|
||||
|
||||
query := &models.LogQueryParam{
|
||||
Username: currentUser.Username,
|
||||
ProjectIDs: []int64{currentProject.ProjectID},
|
||||
}
|
||||
accessLogs, err := GetAccessLogs(query)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in GetAccessLog: %v", err)
|
||||
}
|
||||
if len(accessLogs) != 1 {
|
||||
t.Errorf("The length of accesslog list should be 1, actual: %d", len(accessLogs))
|
||||
}
|
||||
if accessLogs[0].RepoName != projectName+"/" {
|
||||
t.Errorf("The project name does not match, expected: %s, actual: %s", projectName+"/", accessLogs[0].RepoName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTotalOfAccessLogs(t *testing.T) {
|
||||
query := &models.LogQueryParam{
|
||||
Username: currentUser.Username,
|
||||
ProjectIDs: []int64{currentProject.ProjectID},
|
||||
}
|
||||
total, err := GetTotalOfAccessLogs(query)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get total of access log: %v", err)
|
||||
}
|
||||
|
||||
if total != 1 {
|
||||
t.Errorf("unexpected total %d != %d", total, 1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAccessLog(t *testing.T) {
|
||||
var err error
|
||||
var accessLogList []models.AccessLog
|
||||
accessLog := models.AccessLog{
|
||||
Username: currentUser.Username,
|
||||
ProjectID: currentProject.ProjectID,
|
||||
RepoName: currentProject.Name + "/",
|
||||
RepoTag: repoTag,
|
||||
GUID: "N/A",
|
||||
Operation: "create",
|
||||
OpTime: time.Now(),
|
||||
}
|
||||
err = AddAccessLog(accessLog)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in AddAccessLog: %v", err)
|
||||
}
|
||||
|
||||
query := &models.LogQueryParam{
|
||||
Username: accessLog.Username,
|
||||
ProjectIDs: []int64{accessLog.ProjectID},
|
||||
Repository: accessLog.RepoName,
|
||||
Tag: accessLog.RepoTag,
|
||||
Operations: []string{accessLog.Operation},
|
||||
}
|
||||
accessLogList, err = GetAccessLogs(query)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in GetAccessLog: %v", err)
|
||||
}
|
||||
if len(accessLogList) != 1 {
|
||||
t.Errorf("The length of accesslog list should be 1, actual: %d", len(accessLogList))
|
||||
}
|
||||
if accessLogList[0].RepoName != projectName+"/" {
|
||||
t.Errorf("The project name does not match, expected: %s, actual: %s", projectName+"/", accessLogList[0].RepoName)
|
||||
}
|
||||
if accessLogList[0].RepoTag != repoTag {
|
||||
t.Errorf("The repo tag does not match, expected: %s, actual: %s", repoTag, accessLogList[0].RepoTag)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountPull(t *testing.T) {
|
||||
var err error
|
||||
if err = AddAccessLog(models.AccessLog{
|
||||
Username: currentUser.Username,
|
||||
ProjectID: currentProject.ProjectID,
|
||||
RepoName: currentProject.Name + "/tomcat",
|
||||
RepoTag: repoTag2,
|
||||
Operation: "pull",
|
||||
OpTime: time.Now(),
|
||||
}); err != nil {
|
||||
t.Errorf("Error occurred in AccessLog: %v", err)
|
||||
}
|
||||
|
||||
if err = AddAccessLog(models.AccessLog{
|
||||
Username: currentUser.Username,
|
||||
ProjectID: currentProject.ProjectID,
|
||||
RepoName: currentProject.Name + "/tomcat",
|
||||
RepoTag: repoTag2,
|
||||
Operation: "pull",
|
||||
OpTime: time.Now(),
|
||||
}); err != nil {
|
||||
t.Errorf("Error occurred in AccessLog: %v", err)
|
||||
}
|
||||
|
||||
if err = AddAccessLog(models.AccessLog{
|
||||
Username: currentUser.Username,
|
||||
ProjectID: currentProject.ProjectID,
|
||||
RepoName: currentProject.Name + "/tomcat",
|
||||
RepoTag: repoTag2,
|
||||
Operation: "pull",
|
||||
OpTime: time.Now(),
|
||||
}); err != nil {
|
||||
t.Errorf("Error occurred in AccessLog: %v", err)
|
||||
}
|
||||
|
||||
pullCount, err := CountPull(currentProject.Name + "/tomcat")
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in CountPull: %v", err)
|
||||
}
|
||||
if pullCount != 3 {
|
||||
t.Errorf("The access log pull count does not match, expected: 3, actual: %d", pullCount)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestProjectExists(t *testing.T) {
|
||||
var exists bool
|
||||
var err error
|
||||
exists, err = ProjectExists(currentProject.ProjectID)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in ProjectExists: %v", err)
|
||||
}
|
||||
if !exists {
|
||||
t.Errorf("The project with id: %d, does not exist", currentProject.ProjectID)
|
||||
}
|
||||
exists, err = ProjectExists(currentProject.Name)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in ProjectExists: %v", err)
|
||||
}
|
||||
if !exists {
|
||||
t.Errorf("The project with name: %s, does not exist", currentProject.Name)
|
||||
}
|
||||
}
|
||||
*/
|
||||
func TestGetProjectById(t *testing.T) {
|
||||
id := currentProject.ProjectID
|
||||
p, err := GetProjectByID(id)
|
||||
|
@ -61,14 +61,7 @@ func TestDeleteProject(t *testing.T) {
|
||||
}
|
||||
|
||||
func delProjPermanent(id int64) error {
|
||||
_, err := GetOrmer().QueryTable("access_log").
|
||||
Filter("ProjectID", id).
|
||||
Delete()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = GetOrmer().Raw(`delete from project_member
|
||||
_, err := GetOrmer().Raw(`delete from project_member
|
||||
where project_id = ?`, id).Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1,44 +0,0 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// AccessLog holds information about logs which are used to record the actions that user take to the resourses.
|
||||
type AccessLog struct {
|
||||
LogID int `orm:"pk;auto;column(log_id)" json:"log_id"`
|
||||
Username string `orm:"column(username)" json:"username"`
|
||||
ProjectID int64 `orm:"column(project_id)" json:"project_id"`
|
||||
RepoName string `orm:"column(repo_name)" json:"repo_name"`
|
||||
RepoTag string `orm:"column(repo_tag)" json:"repo_tag"`
|
||||
GUID string `orm:"column(guid)" json:"guid"`
|
||||
Operation string `orm:"column(operation)" json:"operation"`
|
||||
OpTime time.Time `orm:"column(op_time)" json:"op_time"`
|
||||
}
|
||||
|
||||
// LogQueryParam is used to set query conditions when listing
|
||||
// access logs.
|
||||
type LogQueryParam struct {
|
||||
ProjectIDs []int64 // the IDs of projects to which the operation is done
|
||||
Username string // the operator's username of the log
|
||||
Repository string // repository name
|
||||
Tag string // tag name
|
||||
Operations []string // operations
|
||||
BeginTime *time.Time // the time after which the operation is done
|
||||
EndTime *time.Time // the time before which the operation is doen
|
||||
Pagination *Pagination // pagination information
|
||||
}
|
@ -23,7 +23,6 @@ func init() {
|
||||
new(User),
|
||||
new(Project),
|
||||
new(Role),
|
||||
new(AccessLog),
|
||||
new(RepoRecord),
|
||||
new(ClairVulnTimestamp),
|
||||
new(ProjectMetadata),
|
||||
|
@ -106,7 +106,6 @@ func init() {
|
||||
beego.Router("/api/users/:id([0-9]+)/password", &UserAPI{}, "put:ChangePassword")
|
||||
beego.Router("/api/users/:id/permissions", &UserAPI{}, "get:ListUserPermissions")
|
||||
beego.Router("/api/users/:id/sysadmin", &UserAPI{}, "put:ToggleUserAdminRole")
|
||||
beego.Router("/api/projects/:id([0-9]+)/logs", &ProjectAPI{}, "get:Logs")
|
||||
beego.Router("/api/projects/:id([0-9]+)/summary", &ProjectAPI{}, "get:Summary")
|
||||
beego.Router("/api/projects/:id([0-9]+)/_deletable", &ProjectAPI{}, "get:Deletable")
|
||||
beego.Router("/api/projects/:id([0-9]+)/metadatas/?:name", &MetadataAPI{}, "get:Get")
|
||||
@ -116,7 +115,6 @@ func init() {
|
||||
beego.Router("/api/statistics", &StatisticAPI{})
|
||||
beego.Router("/api/users/?:id", &UserAPI{})
|
||||
beego.Router("/api/usergroups/?:ugid([0-9]+)", &UserGroupAPI{})
|
||||
beego.Router("/api/logs", &LogAPI{})
|
||||
beego.Router("/api/registries", &RegistryAPI{}, "get:List;post:Post")
|
||||
beego.Router("/api/registries/ping", &RegistryAPI{}, "post:Ping")
|
||||
beego.Router("/api/registries/:id([0-9]+)", &RegistryAPI{}, "get:Get;put:Put;delete:Delete")
|
||||
@ -316,22 +314,6 @@ func (a testapi) StatisticGet(user usrInfo) (int, apilib.StatisticMap, error) {
|
||||
return httpStatusCode, successPayload, err
|
||||
}
|
||||
|
||||
func (a testapi) LogGet(user usrInfo) (int, []apilib.AccessLog, error) {
|
||||
_sling := sling.New().Get(a.basePath)
|
||||
|
||||
// create path and map variables
|
||||
path := "/api/logs/"
|
||||
fmt.Printf("logs path: %s\n", path)
|
||||
_sling = _sling.Path(path)
|
||||
|
||||
var successPayload []apilib.AccessLog
|
||||
code, body, err := request(_sling, jsonAcceptHeader, user)
|
||||
if 200 == code && nil == err {
|
||||
err = json.Unmarshal(body, &successPayload)
|
||||
}
|
||||
return code, successPayload, err
|
||||
}
|
||||
|
||||
// Delete project by projectID
|
||||
func (a testapi) ProjectsDelete(prjUsr usrInfo, projectID string) (int, error) {
|
||||
_sling := sling.New().Delete(a.basePath)
|
||||
@ -413,15 +395,6 @@ func (a testapi) ProjectsPut(prjUsr usrInfo, projectID string,
|
||||
|
||||
}
|
||||
|
||||
// Get access logs accompany with a relevant project.
|
||||
func (a testapi) ProjectLogs(prjUsr usrInfo, projectID string, query *apilib.LogQuery) (int, []byte, error) {
|
||||
_sling := sling.New().Get(a.basePath).
|
||||
Path("/api/projects/" + projectID + "/logs").
|
||||
QueryStruct(query)
|
||||
|
||||
return request(_sling, jsonAcceptHeader, prjUsr)
|
||||
}
|
||||
|
||||
// ProjectDeletable check whether a project can be deleted
|
||||
func (a testapi) ProjectDeletable(prjUsr usrInfo, projectID int64) (int, bool, error) {
|
||||
_sling := sling.New().Get(a.basePath).
|
||||
|
@ -1,127 +0,0 @@
|
||||
// Copyright 2018 Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/common/utils"
|
||||
)
|
||||
|
||||
// LogAPI handles request api/logs
|
||||
type LogAPI struct {
|
||||
BaseController
|
||||
username string
|
||||
isSysAdmin bool
|
||||
}
|
||||
|
||||
// Prepare validates the URL and the user
|
||||
func (l *LogAPI) Prepare() {
|
||||
l.BaseController.Prepare()
|
||||
if !l.SecurityCtx.IsAuthenticated() {
|
||||
l.SendUnAuthorizedError(errors.New("Unauthorized"))
|
||||
return
|
||||
}
|
||||
l.username = l.SecurityCtx.GetUsername()
|
||||
l.isSysAdmin = l.SecurityCtx.IsSysAdmin()
|
||||
}
|
||||
|
||||
// Get returns the recent logs according to parameters
|
||||
func (l *LogAPI) Get() {
|
||||
page, size, err := l.GetPaginationParams()
|
||||
if err != nil {
|
||||
l.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
query := &models.LogQueryParam{
|
||||
Username: l.GetString("username"),
|
||||
Repository: l.GetString("repository"),
|
||||
Tag: l.GetString("tag"),
|
||||
Operations: l.GetStrings("operation"),
|
||||
Pagination: &models.Pagination{
|
||||
Page: page,
|
||||
Size: size,
|
||||
},
|
||||
}
|
||||
|
||||
timestamp := l.GetString("begin_timestamp")
|
||||
if len(timestamp) > 0 {
|
||||
t, err := utils.ParseTimeStamp(timestamp)
|
||||
if err != nil {
|
||||
l.SendBadRequestError(fmt.Errorf("invalid begin_timestamp: %s", timestamp))
|
||||
return
|
||||
}
|
||||
query.BeginTime = t
|
||||
}
|
||||
|
||||
timestamp = l.GetString("end_timestamp")
|
||||
if len(timestamp) > 0 {
|
||||
t, err := utils.ParseTimeStamp(timestamp)
|
||||
if err != nil {
|
||||
l.SendBadRequestError(fmt.Errorf("invalid end_timestamp: %s", timestamp))
|
||||
return
|
||||
}
|
||||
query.EndTime = t
|
||||
}
|
||||
|
||||
if !l.isSysAdmin {
|
||||
projects, err := l.SecurityCtx.GetMyProjects()
|
||||
if err != nil {
|
||||
l.SendInternalServerError(fmt.Errorf(
|
||||
"failed to get projects of user %s: %v", l.username, err))
|
||||
return
|
||||
}
|
||||
|
||||
ids := []int64{}
|
||||
for _, project := range projects {
|
||||
if hasPermission, _ := l.HasProjectPermission(project.ProjectID, rbac.ActionList, rbac.ResourceLog); hasPermission {
|
||||
ids = append(ids, project.ProjectID)
|
||||
}
|
||||
}
|
||||
|
||||
if len(ids) == 0 {
|
||||
l.SetPaginationHeader(0, page, size)
|
||||
l.Data["json"] = nil
|
||||
l.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
query.ProjectIDs = ids
|
||||
}
|
||||
|
||||
total, err := dao.GetTotalOfAccessLogs(query)
|
||||
if err != nil {
|
||||
l.SendInternalServerError(fmt.Errorf(
|
||||
"failed to get total of access logs: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
logs, err := dao.GetAccessLogs(query)
|
||||
if err != nil {
|
||||
l.SendInternalServerError(fmt.Errorf(
|
||||
"failed to get access logs: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
l.SetPaginationHeader(total, page, size)
|
||||
|
||||
l.Data["json"] = logs
|
||||
l.ServeJSON()
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
// Copyright 2018 Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLogGet(t *testing.T) {
|
||||
fmt.Println("Testing Log API")
|
||||
|
||||
var projectID int64 = 1
|
||||
username := "user_for_testing_log_api"
|
||||
repository := "repository_for_testing_log_api"
|
||||
tag := "tag_for_testing_log_api"
|
||||
operation := "op_for_test_log_api"
|
||||
now := time.Now()
|
||||
err := dao.AddAccessLog(models.AccessLog{
|
||||
ProjectID: projectID,
|
||||
Username: username,
|
||||
RepoName: repository,
|
||||
RepoTag: tag,
|
||||
Operation: operation,
|
||||
OpTime: now,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer dao.GetOrmer().QueryTable(&models.AccessLog{}).
|
||||
Filter("username", username).Delete()
|
||||
|
||||
url := "/api/logs"
|
||||
// 401
|
||||
cc := &codeCheckingCase{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: url,
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
}
|
||||
runCodeCheckingCases(t, cc)
|
||||
|
||||
// 200, empty log list
|
||||
c := &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: url,
|
||||
credential: nonSysAdmin,
|
||||
queryStruct: struct {
|
||||
Username string `url:"username"`
|
||||
Repository string `url:"repository"`
|
||||
Tag string `url:"tag"`
|
||||
Operation string `url:"operation"`
|
||||
BeginTimestamp int64 `url:"begin_timestamp"`
|
||||
EndTimestamp int64 `url:"end_timestamp"`
|
||||
}{
|
||||
Username: username,
|
||||
Repository: repository,
|
||||
Tag: tag,
|
||||
Operation: operation,
|
||||
BeginTimestamp: now.Add(-1 * time.Second).Unix(),
|
||||
EndTimestamp: now.Add(1 * time.Second).Unix(),
|
||||
},
|
||||
}
|
||||
logs := []*models.AccessLog{}
|
||||
err = handleAndParse(c, &logs)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 0, len(logs))
|
||||
|
||||
// 200
|
||||
c.credential = projGuest
|
||||
err = handleAndParse(c, &logs)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(logs))
|
||||
assert.Equal(t, projectID, logs[0].ProjectID)
|
||||
assert.Equal(t, username, logs[0].Username)
|
||||
assert.Equal(t, repository, logs[0].RepoName)
|
||||
assert.Equal(t, tag, logs[0].RepoTag)
|
||||
assert.Equal(t, operation, logs[0].Operation)
|
||||
|
||||
// Limited Guest 200 && no logs
|
||||
c.credential = projLimitedGuest
|
||||
err = handleAndParse(c, &logs)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 0, len(logs))
|
||||
}
|
@ -496,68 +496,6 @@ func (p *ProjectAPI) Put() {
|
||||
}
|
||||
}
|
||||
|
||||
// Logs ...
|
||||
func (p *ProjectAPI) Logs() {
|
||||
if !p.requireAccess(rbac.ActionList, rbac.ResourceLog) {
|
||||
return
|
||||
}
|
||||
|
||||
page, size, err := p.GetPaginationParams()
|
||||
if err != nil {
|
||||
p.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
query := &models.LogQueryParam{
|
||||
ProjectIDs: []int64{p.project.ProjectID},
|
||||
Username: p.GetString("username"),
|
||||
Repository: p.GetString("repository"),
|
||||
Tag: p.GetString("tag"),
|
||||
Operations: p.GetStrings("operation"),
|
||||
Pagination: &models.Pagination{
|
||||
Page: page,
|
||||
Size: size,
|
||||
},
|
||||
}
|
||||
|
||||
timestamp := p.GetString("begin_timestamp")
|
||||
if len(timestamp) > 0 {
|
||||
t, err := utils.ParseTimeStamp(timestamp)
|
||||
if err != nil {
|
||||
p.SendBadRequestError(fmt.Errorf("invalid begin_timestamp: %s", timestamp))
|
||||
return
|
||||
}
|
||||
query.BeginTime = t
|
||||
}
|
||||
|
||||
timestamp = p.GetString("end_timestamp")
|
||||
if len(timestamp) > 0 {
|
||||
t, err := utils.ParseTimeStamp(timestamp)
|
||||
if err != nil {
|
||||
p.SendBadRequestError(fmt.Errorf("invalid end_timestamp: %s", timestamp))
|
||||
return
|
||||
}
|
||||
query.EndTime = t
|
||||
}
|
||||
|
||||
total, err := dao.GetTotalOfAccessLogs(query)
|
||||
if err != nil {
|
||||
p.SendInternalServerError(fmt.Errorf(
|
||||
"failed to get total of access log: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
logs, err := dao.GetAccessLogs(query)
|
||||
if err != nil {
|
||||
p.SendInternalServerError(fmt.Errorf(
|
||||
"failed to get access log: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
p.SetPaginationHeader(total, page, size)
|
||||
p.Data["json"] = logs
|
||||
p.ServeJSON()
|
||||
}
|
||||
|
||||
// Summary returns the summary of the project
|
||||
func (p *ProjectAPI) Summary() {
|
||||
if !p.requireAccess(rbac.ActionRead) {
|
||||
|
@ -16,16 +16,14 @@ package api
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/testing/apitests/apilib"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var addProject *apilib.ProjectReq
|
||||
@ -410,53 +408,6 @@ func TestPut(t *testing.T) {
|
||||
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
func TestProjectLogsFilter(t *testing.T) {
|
||||
fmt.Println("\nTest for search access logs filtered by operations and date time ranges..")
|
||||
assert := assert.New(t)
|
||||
|
||||
apiTest := newHarborAPI()
|
||||
|
||||
query := &apilib.LogQuery{
|
||||
Username: "admin",
|
||||
Repository: "",
|
||||
Tag: "",
|
||||
Operation: []string{""},
|
||||
BeginTimestamp: 0,
|
||||
EndTimestamp: time.Now().Unix(),
|
||||
}
|
||||
|
||||
// -------------------case1: Response Code=200------------------------------//
|
||||
fmt.Println("case 1: response code:200")
|
||||
projectID := "1"
|
||||
httpStatusCode, _, err := apiTest.ProjectLogs(*admin, projectID, query)
|
||||
if err != nil {
|
||||
t.Error("Error while search access logs")
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
|
||||
}
|
||||
// -------------------case2: Response Code=401:User need to log in first.------------------------------//
|
||||
fmt.Println("case 2: response code:401:User need to log in first.")
|
||||
projectID = "1"
|
||||
httpStatusCode, _, err = apiTest.ProjectLogs(*unknownUsr, projectID, query)
|
||||
if err != nil {
|
||||
t.Error("Error while search access logs")
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(int(401), httpStatusCode, "httpStatusCode should be 401")
|
||||
}
|
||||
// -------------------case3: Response Code=404:Project does not exist.-------------------------//
|
||||
fmt.Println("case 3: response code:404:Illegal format of provided ID value.")
|
||||
projectID = "11111"
|
||||
httpStatusCode, _, err = apiTest.ProjectLogs(*admin, projectID, query)
|
||||
if err != nil {
|
||||
t.Error("Error while search access logs")
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(int(404), httpStatusCode, "httpStatusCode should be 404")
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
func TestDeletable(t *testing.T) {
|
||||
apiTest := newHarborAPI()
|
||||
|
@ -38,10 +38,6 @@ func TestMain(m *testing.M) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = dao.ClearTable("access_log")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = dao.ClearTable("project")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -44,7 +44,6 @@ func registerLegacyRoutes() {
|
||||
beego.Router("/api/"+version+"/search", &api.SearchAPI{})
|
||||
beego.Router("/api/"+version+"/projects/", &api.ProjectAPI{}, "get:List;post:Post")
|
||||
beego.Router("/api/"+version+"/projects/:id([0-9]+)/summary", &api.ProjectAPI{}, "get:Summary")
|
||||
beego.Router("/api/"+version+"/projects/:id([0-9]+)/logs", &api.ProjectAPI{}, "get:Logs")
|
||||
beego.Router("/api/"+version+"/projects/:id([0-9]+)/_deletable", &api.ProjectAPI{}, "get:Deletable")
|
||||
beego.Router("/api/"+version+"/projects/:id([0-9]+)/metadatas/?:name", &api.MetadataAPI{}, "get:Get")
|
||||
beego.Router("/api/"+version+"/projects/:id([0-9]+)/metadatas/", &api.MetadataAPI{}, "post:Post")
|
||||
@ -62,8 +61,6 @@ func registerLegacyRoutes() {
|
||||
beego.Router("/api/"+version+"/system/CVEWhitelist", &api.SysCVEWhitelistAPI{}, "get:Get;put:Put")
|
||||
beego.Router("/api/"+version+"/system/oidc/ping", &api.OIDCAPI{}, "post:Ping")
|
||||
|
||||
beego.Router("/api/"+version+"/logs", &api.LogAPI{})
|
||||
|
||||
beego.Router("/api/"+version+"/replication/adapters", &api.ReplicationAdapterAPI{}, "get:List")
|
||||
beego.Router("/api/"+version+"/replication/adapterinfos", &api.ReplicationAdapterAPI{}, "get:ListAdapterInfos")
|
||||
beego.Router("/api/"+version+"/replication/executions", &api.ReplicationOperationAPI{}, "get:ListExecutions;post:CreateExecution")
|
||||
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type AccessLog struct {
|
||||
|
||||
// The ID of the log entry.
|
||||
LogId int32 `json:"log_id,omitempty"`
|
||||
|
||||
// Name of the repository in this log entry.
|
||||
RepoName string `json:"repo_name,omitempty"`
|
||||
|
||||
// Tag of the repository in this log entry.
|
||||
RepoTag string `json:"repo_tag,omitempty"`
|
||||
|
||||
// The operation against the repository in this log entry.
|
||||
Operation string `json:"operation,omitempty"`
|
||||
|
||||
// The time when this operation is triggered.
|
||||
OpTime string `json:"op_time,omitempty"`
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type LogQuery struct {
|
||||
Username string `url:"username,omitempty"`
|
||||
Repository string `url:"repository,omitempty"`
|
||||
Tag string `url:"tag,omitempty"`
|
||||
Operation []string `url:"operation,omitempty"`
|
||||
BeginTimestamp int64 `url:"begin_timestamp,omitempty"`
|
||||
EndTimestamp int64 `url:"end_timestamp,omitempty"`
|
||||
Page int64 `url:"page,omitempty"`
|
||||
PageSize int64 `url:"page_size,omitempty"`
|
||||
}
|
@ -118,10 +118,11 @@ class TestAssignRoleToLdapGroup(unittest.TestCase):
|
||||
self.dockerCmdLoginDev(username="dev_user", password="zhu88jie")
|
||||
self.dockerCmdLoginGuest(username="guest_user", password="zhu88jie")
|
||||
|
||||
self.assertTrue(self.queryUserLogs(username="admin_user", password="zhu88jie")>0, "admin user can see logs")
|
||||
self.assertTrue(self.queryUserLogs(username="dev_user", password="zhu88jie")>0, "dev user can see logs")
|
||||
self.assertTrue(self.queryUserLogs(username="guest_user", password="zhu88jie")>0, "guest user can see logs")
|
||||
self.assertTrue(self.queryUserLogs(username="test", password="123456")==0, "test user can not see any logs")
|
||||
#ToDo, enable them
|
||||
#self.assertTrue(self.queryUserLogs(username="admin_user", password="zhu88jie")>0, "admin user can see logs")
|
||||
#self.assertTrue(self.queryUserLogs(username="dev_user", password="zhu88jie")>0, "dev user can see logs")
|
||||
#self.assertTrue(self.queryUserLogs(username="guest_user", password="zhu88jie")>0, "guest user can see logs")
|
||||
#self.assertTrue(self.queryUserLogs(username="test", password="123456")==0, "test user can not see any logs")
|
||||
|
||||
pass
|
||||
|
||||
|
@ -20,9 +20,8 @@ sys.path.append(os.environ["SWAGGER_CLIENT_PATH"])
|
||||
import unittest
|
||||
import testutils
|
||||
import swagger_client
|
||||
from swagger_client.models.project_req import ProjectReq
|
||||
from swagger_client.models.access_log import AccessLog
|
||||
from swagger_client.models.configurations import Configurations
|
||||
from swagger_client.models.project_req import ProjectReq
|
||||
from swagger_client.models.configurations import Configurations
|
||||
from swagger_client.rest import ApiException
|
||||
from swagger_client.models.configurations import Configurations
|
||||
from pprint import pprint
|
||||
|
Loading…
Reference in New Issue
Block a user