diff --git a/api/base.go b/api/base.go index 2262bb3ac..f7b84d8b1 100644 --- a/api/base.go +++ b/api/base.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package api import ( @@ -24,19 +25,23 @@ import ( "github.com/astaxie/beego" ) +// BaseAPI wraps common methods for controllers to host API type BaseAPI struct { beego.Controller } +// Render returns nil as it won't render template func (b *BaseAPI) Render() error { return nil } +// RenderError provides shortcut to render http error func (b *BaseAPI) RenderError(code int, text string) { http.Error(b.Ctx.ResponseWriter, text, code) } -func (b *BaseAPI) DecodeJsonReq(v interface{}) { +// DecodeJSONReq decodes a json request +func (b *BaseAPI) DecodeJSONReq(v interface{}) { err := json.Unmarshal(b.Ctx.Input.CopyBody(1<<32), v) if err != nil { beego.Error("Error while decoding the json request:", err) @@ -44,22 +49,23 @@ func (b *BaseAPI) DecodeJsonReq(v interface{}) { } } +// ValidateUser checks if the request triggered by a valid user func (b *BaseAPI) ValidateUser() int { - sessionUserId := b.GetSession("userId") - if sessionUserId == nil { + sessionUserID := b.GetSession("userId") + if sessionUserID == nil { beego.Warning("No user id in session, canceling request") b.CustomAbort(http.StatusUnauthorized, "") } - userId := sessionUserId.(int) - u, err := dao.GetUser(models.User{UserId: userId}) + userID := sessionUserID.(int) + u, err := dao.GetUser(models.User{UserID: userID}) if err != nil { beego.Error("Error occurred in GetUser:", err) b.CustomAbort(http.StatusInternalServerError, "Internal error.") } if u == nil { - beego.Warning("User was deleted already, user id: ", userId, " canceling request.") + beego.Warning("User was deleted already, user id: ", userID, " canceling request.") b.CustomAbort(http.StatusUnauthorized, "") } - return userId + return userID } diff --git a/api/project_member.go b/api/member.go similarity index 77% rename from api/project_member.go rename to api/member.go index e20d020ef..2e9243d12 100644 --- a/api/project_member.go +++ b/api/member.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package api import ( @@ -24,19 +25,21 @@ import ( "github.com/astaxie/beego" ) +// ProjectMemberAPI handles request to /api/projects/{}/members/{} type ProjectMemberAPI struct { BaseAPI - memberId int - currentUserId int + memberID int + currentUserID int project *models.Project } type memberReq struct { Username string `json:"user_name"` - UserId int `json:"user_id"` + UserID int `json:"user_id"` Roles []int `json:"roles"` } +// Prepare validates the URL and parms func (pma *ProjectMemberAPI) Prepare() { pid, err := strconv.ParseInt(pma.Ctx.Input.Param(":pid"), 10, 64) if err != nil { @@ -44,7 +47,7 @@ func (pma *ProjectMemberAPI) Prepare() { pma.CustomAbort(http.StatusBadRequest, "invalid project Id") return } - p, err := dao.GetProjectById(pid) + p, err := dao.GetProjectByID(pid) if err != nil { beego.Error("Error occurred in GetProjectById:", err) pma.CustomAbort(http.StatusInternalServerError, "Internal error.") @@ -55,30 +58,31 @@ func (pma *ProjectMemberAPI) Prepare() { pma.CustomAbort(http.StatusNotFound, "Project does not exist") } pma.project = p - pma.currentUserId = pma.ValidateUser() + pma.currentUserID = pma.ValidateUser() mid := pma.Ctx.Input.Param(":mid") if mid == "current" { - pma.memberId = pma.currentUserId + pma.memberID = pma.currentUserID } else if len(mid) == 0 { - pma.memberId = 0 + pma.memberID = 0 } else if len(mid) > 0 { - memberId, err := strconv.Atoi(mid) + memberID, err := strconv.Atoi(mid) if err != nil { beego.Error("Invalid member Id, error:", err) pma.CustomAbort(http.StatusBadRequest, "Invalid member id") } - pma.memberId = memberId + pma.memberID = memberID } } +// Get ... func (pma *ProjectMemberAPI) Get() { - pid := pma.project.ProjectId - if !CheckProjectPermission(pma.currentUserId, pid) { - beego.Warning("Current user, user id :", pma.currentUserId, "does not have permission for project, id:", pid) + pid := pma.project.ProjectID + if !checkProjectPermission(pma.currentUserID, pid) { + beego.Warning("Current user, user id :", pma.currentUserID, "does not have permission for project, id:", pid) pma.RenderError(http.StatusForbidden, "") return } - if pma.memberId == 0 { //member id not set return list of the members + if pma.memberID == 0 { //member id not set return list of the members username := pma.GetString("username") queryUser := models.User{Username: "%" + username + "%"} userList, err := dao.GetUserByProject(pid, queryUser) @@ -89,86 +93,88 @@ func (pma *ProjectMemberAPI) Get() { } pma.Data["json"] = userList } else { //return detail of a member - roleList, err := dao.GetUserProjectRoles(models.User{UserId: pma.memberId}, pid) + roleList, err := dao.GetUserProjectRoles(models.User{UserID: pma.memberID}, pid) if err != nil { beego.Error("Error occurred in GetUserProjectRoles:", err) pma.CustomAbort(http.StatusInternalServerError, "Internal error.") } //return empty role list to indicate if a user is not a member result := make(map[string]interface{}) - user, err := dao.GetUser(models.User{UserId: pma.memberId}) + user, err := dao.GetUser(models.User{UserID: pma.memberID}) if err != nil { beego.Error("Error occurred in GetUser:", err) pma.CustomAbort(http.StatusInternalServerError, "Internal error.") } result["user_name"] = user.Username - result["user_id"] = pma.memberId + result["user_id"] = pma.memberID result["roles"] = roleList pma.Data["json"] = result } pma.ServeJSON() } +// Post ... func (pma *ProjectMemberAPI) Post() { - pid := pma.project.ProjectId - userQuery := models.User{UserId: pma.currentUserId, RoleId: models.PROJECTADMIN} + pid := pma.project.ProjectID + userQuery := models.User{UserID: pma.currentUserID, RoleID: models.PROJECTADMIN} rolelist, err := dao.GetUserProjectRoles(userQuery, pid) if err != nil { beego.Error("Error occurred in GetUserProjectRoles:", err) pma.CustomAbort(http.StatusInternalServerError, "Internal error.") } if len(rolelist) == 0 { - beego.Warning("Current user, id:", pma.currentUserId, "does not have project admin role for project, id:", pid) + beego.Warning("Current user, id:", pma.currentUserID, "does not have project admin role for project, id:", pid) pma.RenderError(http.StatusForbidden, "") return } var req memberReq - pma.DecodeJsonReq(&req) + pma.DecodeJSONReq(&req) username := req.Username - userId := CheckUserExists(username) - if userId <= 0 { + userID := checkUserExists(username) + if userID <= 0 { beego.Warning("User does not exist, user name:", username) pma.RenderError(http.StatusNotFound, "User does not exist") return } - rolelist, err = dao.GetUserProjectRoles(models.User{UserId: userId}, pid) + rolelist, err = dao.GetUserProjectRoles(models.User{UserID: userID}, pid) if err != nil { beego.Error("Error occurred in GetUserProjectRoles:", err) pma.CustomAbort(http.StatusInternalServerError, "Internal error.") } if len(rolelist) > 0 { - beego.Warning("user is already added to project, user id:", userId, ", project id:", pid) + beego.Warning("user is already added to project, user id:", userID, ", project id:", pid) pma.RenderError(http.StatusConflict, "user is ready in project") return } for _, rid := range req.Roles { - err = dao.AddUserProjectRole(userId, pid, int(rid)) + err = dao.AddUserProjectRole(userID, pid, int(rid)) if err != nil { - beego.Error("Failed to update DB to add project user role, project id:", pid, ", user id:", userId, ", role id:", rid) + beego.Error("Failed to update DB to add project user role, project id:", pid, ", user id:", userID, ", role id:", rid) pma.RenderError(http.StatusInternalServerError, "Failed to update data in database") return } } } +// Put ... func (pma *ProjectMemberAPI) Put() { - pid := pma.project.ProjectId - mid := pma.memberId - userQuery := models.User{UserId: pma.currentUserId, RoleId: models.PROJECTADMIN} + pid := pma.project.ProjectID + mid := pma.memberID + userQuery := models.User{UserID: pma.currentUserID, RoleID: models.PROJECTADMIN} rolelist, err := dao.GetUserProjectRoles(userQuery, pid) if err != nil { beego.Error("Error occurred in GetUserProjectRoles:", err) pma.CustomAbort(http.StatusInternalServerError, "Internal error.") } if len(rolelist) == 0 { - beego.Warning("Current user, id:", pma.currentUserId, ", does not have project admin role for project, id:", pid) + beego.Warning("Current user, id:", pma.currentUserID, ", does not have project admin role for project, id:", pid) pma.RenderError(http.StatusForbidden, "") return } var req memberReq - pma.DecodeJsonReq(&req) - roleList, err := dao.GetUserProjectRoles(models.User{UserId: mid}, pid) + pma.DecodeJSONReq(&req) + roleList, err := dao.GetUserProjectRoles(models.User{UserID: mid}, pid) if len(roleList) == 0 { beego.Warning("User is not in project, user id:", mid, ", project id:", pid) pma.RenderError(http.StatusNotFound, "user not exist in project") @@ -193,13 +199,14 @@ func (pma *ProjectMemberAPI) Put() { } } +// Delete ... func (pma *ProjectMemberAPI) Delete() { - pid := pma.project.ProjectId - mid := pma.memberId - userQuery := models.User{UserId: pma.currentUserId, RoleId: models.PROJECTADMIN} + pid := pma.project.ProjectID + mid := pma.memberID + userQuery := models.User{UserID: pma.currentUserID, RoleID: models.PROJECTADMIN} rolelist, err := dao.GetUserProjectRoles(userQuery, pid) if len(rolelist) == 0 { - beego.Warning("Current user, id:", pma.currentUserId, ", does not have project admin role for project, id:", pid) + beego.Warning("Current user, id:", pma.currentUserID, ", does not have project admin role for project, id:", pid) pma.RenderError(http.StatusForbidden, "") return } diff --git a/api/project.go b/api/project.go index 42944ef51..fc8c86f33 100644 --- a/api/project.go +++ b/api/project.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package api import ( @@ -28,10 +29,11 @@ import ( "github.com/astaxie/beego" ) +// ProjectAPI handles request to /api/projects/{} /api/projects/{}/logs type ProjectAPI struct { BaseAPI - userId int - projectId int64 + userID int + projectID int64 } type projectReq struct { @@ -39,33 +41,35 @@ type projectReq struct { Public bool `json:"public"` } -const PROJECT_NAME_MAX_LEN int = 30 +const projectNameMaxLen int = 30 +// Prepare validates the URL and the user func (p *ProjectAPI) Prepare() { - p.userId = p.ValidateUser() - id_str := p.Ctx.Input.Param(":id") - if len(id_str) > 0 { + p.userID = p.ValidateUser() + idStr := p.Ctx.Input.Param(":id") + if len(idStr) > 0 { var err error - p.projectId, err = strconv.ParseInt(id_str, 10, 64) + p.projectID, err = strconv.ParseInt(idStr, 10, 64) if err != nil { - log.Printf("Error parsing project id: %s, error: %v", id_str, err) + log.Printf("Error parsing project id: %s, error: %v", idStr, err) p.CustomAbort(http.StatusBadRequest, "invalid project id") } - exist, err := dao.ProjectExists(p.projectId) + exist, err := dao.ProjectExists(p.projectID) if err != nil { log.Printf("Error occurred in ProjectExists: %v", err) p.CustomAbort(http.StatusInternalServerError, "Internal error.") } if !exist { - p.CustomAbort(http.StatusNotFound, fmt.Sprintf("project does not exist, id: %v", p.projectId)) + p.CustomAbort(http.StatusNotFound, fmt.Sprintf("project does not exist, id: %v", p.projectID)) } } } +// Post ... func (p *ProjectAPI) Post() { var req projectReq var public int - p.DecodeJsonReq(&req) + p.DecodeJSONReq(&req) if req.Public { public = 1 } @@ -84,14 +88,15 @@ func (p *ProjectAPI) Post() { p.RenderError(http.StatusConflict, "") return } - project := models.Project{OwnerId: p.userId, Name: projectName, CreationTime: time.Now(), Public: public} + project := models.Project{OwnerID: p.userID, Name: projectName, CreationTime: time.Now(), Public: public} err = dao.AddProject(project) if err != nil { - beego.Error("Failed to add project, error: %v", err) + beego.Error("Failed to add project, error: ", err) p.RenderError(http.StatusInternalServerError, "Failed to add project") } } +// Head ... func (p *ProjectAPI) Head() { projectName := p.GetString("project_name") result, err := dao.ProjectExists(projectName) @@ -106,8 +111,9 @@ func (p *ProjectAPI) Head() { } } +// Get ... func (p *ProjectAPI) Get() { - queryProject := models.Project{UserId: p.userId} + queryProject := models.Project{UserID: p.userID} projectName := p.GetString("project_name") if len(projectName) > 0 { queryProject.Name = "%" + projectName + "%" @@ -121,7 +127,7 @@ func (p *ProjectAPI) Get() { p.CustomAbort(http.StatusInternalServerError, "Internal error.") } for i := 0; i < len(projectList); i++ { - if isProjectAdmin(p.userId, projectList[i].ProjectId) { + if isProjectAdmin(p.userID, projectList[i].ProjectID) { projectList[i].Togglable = true } } @@ -129,37 +135,39 @@ func (p *ProjectAPI) Get() { p.ServeJSON() } +// Put ... func (p *ProjectAPI) Put() { var req projectReq var public int - projectId, err := strconv.ParseInt(p.Ctx.Input.Param(":id"), 10, 64) + projectID, err := strconv.ParseInt(p.Ctx.Input.Param(":id"), 10, 64) if err != nil { - beego.Error("Error parsing project id:", projectId, ", error: ", err) + beego.Error("Error parsing project id:", projectID, ", error: ", err) p.RenderError(http.StatusBadRequest, "invalid project id") return } - p.DecodeJsonReq(&req) + p.DecodeJSONReq(&req) if req.Public { public = 1 } - if !isProjectAdmin(p.userId, projectId) { - beego.Warning("Current user, id:", p.userId, ", does not have project admin role for project, id:", projectId) + if !isProjectAdmin(p.userID, projectID) { + beego.Warning("Current user, id:", p.userID, ", does not have project admin role for project, id:", projectID) p.RenderError(http.StatusForbidden, "") return } - err = dao.ToggleProjectPublicity(p.projectId, public) + err = dao.ToggleProjectPublicity(p.projectID, public) if err != nil { - beego.Error("Error while updating project, project id:", projectId, ", error:", err) + beego.Error("Error while updating project, project id:", projectID, ", error:", err) p.RenderError(http.StatusInternalServerError, "Failed to update project") } } +// FilterAccessLog handles GET to /api/projects/{}/logs func (p *ProjectAPI) FilterAccessLog() { var filter models.AccessLog - p.DecodeJsonReq(&filter) + p.DecodeJSONReq(&filter) username := filter.Username keywords := filter.Keywords @@ -167,7 +175,7 @@ func (p *ProjectAPI) FilterAccessLog() { beginTime := time.Unix(filter.BeginTimestamp, 0) endTime := time.Unix(filter.EndTimestamp, 0) - query := models.AccessLog{ProjectId: p.projectId, Username: "%" + username + "%", Keywords: keywords, BeginTime: beginTime, BeginTimestamp: filter.BeginTimestamp, EndTime: endTime, EndTimestamp: filter.EndTimestamp} + query := models.AccessLog{ProjectID: p.projectID, Username: "%" + username + "%", Keywords: keywords, BeginTime: beginTime, BeginTimestamp: filter.BeginTimestamp, EndTime: endTime, EndTimestamp: filter.EndTimestamp} log.Printf("Query AccessLog: begin: %v, end: %v, keywords: %s", query.BeginTime, query.EndTime, query.Keywords) @@ -180,8 +188,8 @@ func (p *ProjectAPI) FilterAccessLog() { p.ServeJSON() } -func isProjectAdmin(userId int, pid int64) bool { - userQuery := models.User{UserId: userId, RoleId: models.PROJECTADMIN} +func isProjectAdmin(userID int, pid int64) bool { + userQuery := models.User{UserID: userID, RoleID: models.PROJECTADMIN} rolelist, err := dao.GetUserProjectRoles(userQuery, pid) if err != nil { beego.Error("Error occurred in GetUserProjectRoles:", err, ", returning false") @@ -195,7 +203,7 @@ func validateProjectReq(req projectReq) error { if len(pn) == 0 { return fmt.Errorf("Project name can not be empty") } - if len(pn) > PROJECT_NAME_MAX_LEN { + if len(pn) > projectNameMaxLen { return fmt.Errorf("Project name is too long") } return nil diff --git a/api/repository.go b/api/repository.go index 7887647a9..7fec81068 100644 --- a/api/repository.go +++ b/api/repository.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package api import ( @@ -28,21 +29,23 @@ import ( "github.com/astaxie/beego" ) -//For repostiories, we won't check the session in this API due to search functionality, querying manifest will be contorlled by -//the security of registry - +// RepositoryAPI handles request to /api/repositories /api/repositories/tags /api/repositories/manifests, the parm has to be put +// in the query string as the web framework can not parse the URL if it contains veriadic sectors. +// For repostiories, we won't check the session in this API due to search functionality, querying manifest will be contorlled by +// the security of registry type RepositoryAPI struct { BaseAPI - userId int + userID int username string } +// Prepare will set a non existent user ID in case the request tries to view repositories under a project he doesn't has permission. func (ra *RepositoryAPI) Prepare() { - userId, ok := ra.GetSession("userId").(int) + userID, ok := ra.GetSession("userId").(int) if !ok { - ra.userId = dao.NON_EXIST_USER_ID + ra.userID = dao.NonExistUserID } else { - ra.userId = userId + ra.userID = userID } username, ok := ra.GetSession("username").(string) if !ok { @@ -53,24 +56,25 @@ func (ra *RepositoryAPI) Prepare() { } } +// Get ... func (ra *RepositoryAPI) Get() { - projectId, err0 := ra.GetInt64("project_id") + projectID, err0 := ra.GetInt64("project_id") if err0 != nil { beego.Error("Failed to get project id, error:", err0) ra.RenderError(http.StatusBadRequest, "Invalid project id") return } - p, err := dao.GetProjectById(projectId) + p, err := dao.GetProjectByID(projectID) if err != nil { beego.Error("Error occurred in GetProjectById:", err) ra.CustomAbort(http.StatusInternalServerError, "Internal error.") } if p == nil { - beego.Warning("Project with Id:", projectId, ", does not exist", projectId) + beego.Warning("Project with Id:", projectID, ", does not exist") ra.RenderError(http.StatusNotFound, "") return } - if p.Public == 0 && !CheckProjectPermission(ra.userId, projectId) { + if p.Public == 0 && !checkProjectPermission(ra.userID, projectID) { ra.RenderError(http.StatusForbidden, "") return } @@ -102,34 +106,35 @@ func (ra *RepositoryAPI) Get() { ra.ServeJSON() } -type Tag struct { - Name string `json: "name"` +type tag struct { + Name string `json:"name"` Tags []string `json:"tags"` } -type HistroyItem struct { +type histroyItem struct { V1Compatibility string `json:"v1Compatibility"` } -type Manifest struct { +type manifest struct { Name string `json:"name"` Tag string `json:"tag"` Architecture string `json:"architecture"` SchemaVersion int `json:"schemaVersion"` - History []HistroyItem `json:"history"` + History []histroyItem `json:"history"` } +// GetTags handles GET /api/repositories/tags func (ra *RepositoryAPI) GetTags() { var tags []string repoName := ra.GetString("repo_name") - result, err := svc_utils.RegistryApiGet(svc_utils.BuildRegistryUrl(repoName, "tags", "list"), ra.username) + result, err := svc_utils.RegistryAPIGet(svc_utils.BuildRegistryURL(repoName, "tags", "list"), ra.username) if err != nil { beego.Error("Failed to get repo tags, repo name:", repoName, ", error: ", err) ra.RenderError(http.StatusInternalServerError, "Failed to get repo tags") } else { - t := Tag{} + t := tag{} json.Unmarshal(result, &t) tags = t.Tags } @@ -137,38 +142,36 @@ func (ra *RepositoryAPI) GetTags() { ra.ServeJSON() } +// GetManifests handles GET /api/repositories/manifests func (ra *RepositoryAPI) GetManifests() { repoName := ra.GetString("repo_name") tag := ra.GetString("tag") item := models.RepoItem{} - result, err := svc_utils.RegistryApiGet(svc_utils.BuildRegistryUrl(repoName, "manifests", tag), ra.username) + result, err := svc_utils.RegistryAPIGet(svc_utils.BuildRegistryURL(repoName, "manifests", tag), ra.username) if err != nil { beego.Error("Failed to get manifests for repo, repo name:", repoName, ", tag:", tag, ", error:", err) ra.RenderError(http.StatusInternalServerError, "Internal Server Error") return - } else { - mani := Manifest{} - err = json.Unmarshal(result, &mani) - if err != nil { - beego.Error("Failed to decode json from response for manifests, repo name:", repoName, ", tag:", tag, ", error:", err) - ra.RenderError(http.StatusInternalServerError, "Internal Server Error") - return - } else { - v1Compatibility := mani.History[0].V1Compatibility - - err = json.Unmarshal([]byte(v1Compatibility), &item) - if err != nil { - beego.Error("Failed to decode V1 field for repo, repo name:", repoName, ", tag:", tag, ", error:", err) - ra.RenderError(http.StatusInternalServerError, "Internal Server Error") - return - } else { - item.CreatedStr = item.Created.Format("2006-01-02 15:04:05") - item.DurationDays = strconv.Itoa(int(time.Since(item.Created).Hours()/24)) + " days" - } - } } + mani := manifest{} + err = json.Unmarshal(result, &mani) + if err != nil { + beego.Error("Failed to decode json from response for manifests, repo name:", repoName, ", tag:", tag, ", error:", err) + ra.RenderError(http.StatusInternalServerError, "Internal Server Error") + return + } + v1Compatibility := mani.History[0].V1Compatibility + + err = json.Unmarshal([]byte(v1Compatibility), &item) + if err != nil { + beego.Error("Failed to decode V1 field for repo, repo name:", repoName, ", tag:", tag, ", error:", err) + ra.RenderError(http.StatusInternalServerError, "Internal Server Error") + return + } + item.CreatedStr = item.Created.Format("2006-01-02 15:04:05") + item.DurationDays = strconv.Itoa(int(time.Since(item.Created).Hours()/24)) + " days" ra.Data["json"] = item ra.ServeJSON() diff --git a/api/search.go b/api/search.go index e0d7348bc..fe6f76f26 100644 --- a/api/search.go +++ b/api/search.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package api import ( @@ -27,24 +28,26 @@ import ( "github.com/astaxie/beego" ) +// SearchAPI handles requesst to /api/search type SearchAPI struct { BaseAPI } -type SearchResult struct { +type searchResult struct { Project []map[string]interface{} `json:"project"` Repository []map[string]interface{} `json:"repository"` } +// Get ... func (n *SearchAPI) Get() { - userId, ok := n.GetSession("userId").(int) + userID, ok := n.GetSession("userId").(int) if !ok { - userId = dao.NON_EXIST_USER_ID + userID = dao.NonExistUserID } keyword := n.GetString("q") - projects, err := dao.QueryRelevantProjects(userId) + projects, err := dao.QueryRelevantProjects(userID) if err != nil { - beego.Error("Failed to get projects of user id:", userId, ", error:", err) + beego.Error("Failed to get projects of user id:", userID, ", error:", err) n.CustomAbort(http.StatusInternalServerError, "Failed to get project search result") } projectSorter := &utils.ProjectSorter{Projects: projects} @@ -57,7 +60,7 @@ func (n *SearchAPI) Get() { } if match { entry := make(map[string]interface{}) - entry["id"] = p.ProjectId + entry["id"] = p.ProjectID entry["name"] = p.Name entry["public"] = p.Public projectResult = append(projectResult, entry) @@ -71,7 +74,7 @@ func (n *SearchAPI) Get() { } sort.Strings(repositories) repositoryResult := filterRepositories(repositories, projects, keyword) - result := &SearchResult{Project: projectResult, Repository: repositoryResult} + result := &searchResult{Project: projectResult, Repository: repositoryResult} n.Data["json"] = result n.ServeJSON() } @@ -93,7 +96,7 @@ func filterRepositories(repositories []string, projects []models.Project, keywor entry := make(map[string]interface{}) entry["repository_name"] = r.Name entry["project_name"] = projects[j].Name - entry["project_id"] = projects[j].ProjectId + entry["project_id"] = projects[j].ProjectID entry["project_public"] = projects[j].Public result = append(result, entry) } else { diff --git a/api/user.go b/api/user.go index 1c3fcbc13..e104520c8 100644 --- a/api/user.go +++ b/api/user.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package api import ( @@ -24,48 +25,51 @@ import ( "github.com/astaxie/beego" ) +// UserAPI handles request to /api/users/{} type UserAPI struct { BaseAPI - currentUid int - userId int + currentUserID int + userID int } +// Prepare validates the URL and parms func (ua *UserAPI) Prepare() { - ua.currentUid = ua.ValidateUser() + ua.currentUserID = ua.ValidateUser() id := ua.Ctx.Input.Param(":id") if id == "current" { - ua.userId = ua.currentUid + ua.userID = ua.currentUserID } else if len(id) > 0 { var err error - ua.userId, err = strconv.Atoi(id) + ua.userID, err = strconv.Atoi(id) if err != nil { beego.Error("Invalid user id, error:", err) ua.CustomAbort(http.StatusBadRequest, "Invalid user Id") } - userQuery := models.User{UserId: ua.userId} + userQuery := models.User{UserID: ua.userID} u, err := dao.GetUser(userQuery) if err != nil { beego.Error("Error occurred in GetUser:", err) ua.CustomAbort(http.StatusInternalServerError, "Internal error.") } if u == nil { - beego.Error("User with Id:", ua.userId, "does not exist") + beego.Error("User with Id:", ua.userID, "does not exist") ua.CustomAbort(http.StatusNotFound, "") } } } +// Get ... func (ua *UserAPI) Get() { - exist, err := dao.IsAdminRole(ua.currentUid) + exist, err := dao.IsAdminRole(ua.currentUserID) if err != nil { beego.Error("Error occurred in IsAdminRole:", err) ua.CustomAbort(http.StatusInternalServerError, "Internal error.") } - if ua.userId == 0 { //list users + if ua.userID == 0 { //list users if !exist { - beego.Error("Current user, id:", ua.currentUid, ", does not have admin role, can not list users") + beego.Error("Current user, id:", ua.currentUserID, ", does not have admin role, can not list users") ua.RenderError(http.StatusForbidden, "User does not have admin role") return } @@ -82,8 +86,8 @@ func (ua *UserAPI) Get() { } ua.Data["json"] = userList - } else if ua.userId == ua.currentUid || exist { - userQuery := models.User{UserId: ua.userId} + } else if ua.userID == ua.currentUserID || exist { + userQuery := models.User{UserID: ua.userID} u, err := dao.GetUser(userQuery) if err != nil { beego.Error("Error occurred in GetUser:", err) @@ -91,40 +95,42 @@ func (ua *UserAPI) Get() { } ua.Data["json"] = u } else { - beego.Error("Current user, id:", ua.currentUid, "does not have admin role, can not view other user's detail") + beego.Error("Current user, id:", ua.currentUserID, "does not have admin role, can not view other user's detail") ua.RenderError(http.StatusForbidden, "User does not have admin role") return } ua.ServeJSON() } +// Put ... func (ua *UserAPI) Put() { //currently only for toggle admin, so no request body - exist, err := dao.IsAdminRole(ua.currentUid) + exist, err := dao.IsAdminRole(ua.currentUserID) if err != nil { beego.Error("Error occurred in IsAdminRole:", err) ua.CustomAbort(http.StatusInternalServerError, "Internal error.") } if !exist { - beego.Warning("current user, id:", ua.currentUid, ", does not have admin role, can not update other user's role") + beego.Warning("current user, id:", ua.currentUserID, ", does not have admin role, can not update other user's role") ua.RenderError(http.StatusForbidden, "User does not have admin role") return } - userQuery := models.User{UserId: ua.userId} + userQuery := models.User{UserID: ua.userID} dao.ToggleUserAdminRole(userQuery) } +// Delete ... func (ua *UserAPI) Delete() { - exist, err := dao.IsAdminRole(ua.currentUid) + exist, err := dao.IsAdminRole(ua.currentUserID) if err != nil { beego.Error("Error occurred in IsAdminRole:", err) ua.CustomAbort(http.StatusInternalServerError, "Internal error.") } if !exist { - beego.Warning("current user, id:", ua.currentUid, ", does not have admin role, can not remove user") + beego.Warning("current user, id:", ua.currentUserID, ", does not have admin role, can not remove user") ua.RenderError(http.StatusForbidden, "User does not have admin role") return } - err = dao.DeleteUser(ua.userId) + err = dao.DeleteUser(ua.userID) if err != nil { beego.Error("Failed to delete data from database, error:", err) ua.RenderError(http.StatusInternalServerError, "Failed to delete User") diff --git a/api/utils.go b/api/utils.go index 573c9837c..700bf9fb9 100644 --- a/api/utils.go +++ b/api/utils.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package api import ( @@ -21,8 +22,8 @@ import ( "github.com/astaxie/beego" ) -func CheckProjectPermission(userId int, projectId int64) bool { - exist, err := dao.IsAdminRole(userId) +func checkProjectPermission(userID int, projectID int64) bool { + exist, err := dao.IsAdminRole(userID) if err != nil { beego.Error("Error occurred in IsAdminRole:", err) return false @@ -30,7 +31,7 @@ func CheckProjectPermission(userId int, projectId int64) bool { if exist { return true } - roleList, err := dao.GetUserProjectRoles(models.User{UserId: userId}, projectId) + roleList, err := dao.GetUserProjectRoles(models.User{UserID: userID}, projectID) if err != nil { beego.Error("Error occurred in GetUserProjectRoles:", err) return false @@ -38,14 +39,14 @@ func CheckProjectPermission(userId int, projectId int64) bool { return len(roleList) > 0 } -func CheckUserExists(name string) int { +func checkUserExists(name string) int { u, err := dao.GetUser(models.User{Username: name}) if err != nil { beego.Error("Error occurred in GetUser:", err) return 0 } if u != nil { - return u.UserId + return u.UserID } return 0 } diff --git a/opt_auth/opt_auth.go b/auth/authenticator.go similarity index 54% rename from opt_auth/opt_auth.go rename to auth/authenticator.go index 164a19831..14f881cde 100644 --- a/opt_auth/opt_auth.go +++ b/auth/authenticator.go @@ -12,10 +12,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -package opt_auth + +package auth import ( "fmt" + "log" "os" "github.com/vmware/harbor/models" @@ -23,31 +25,36 @@ import ( "github.com/astaxie/beego" ) -type OptAuth interface { - Validate(auth models.AuthModel) (*models.User, error) +// Authenticator provides interface to authenticate user credentials. +type Authenticator interface { + + // Authenticate ... + Authenticate(m models.AuthModel) (*models.User, error) } -var registry = make(map[string]OptAuth) +var registry = make(map[string]Authenticator) -func Register(name string, optAuth OptAuth) { +// Register add different authenticators to registry map. +func Register(name string, authenticator Authenticator) { if _, dup := registry[name]; dup { - panic(name + " already exist.") + log.Printf("authenticator: %s has been registered", name) return } - registry[name] = optAuth + registry[name] = authenticator } -func Login(auth models.AuthModel) (*models.User, error) { +// Login authenticates user credentials based on setting. +func Login(m models.AuthModel) (*models.User, error) { - var authMode string = os.Getenv("AUTH_MODE") - if authMode == "" || auth.Principal == "admin" { + var authMode = os.Getenv("AUTH_MODE") + if authMode == "" || m.Principal == "admin" { authMode = "db_auth" } beego.Debug("Current AUTH_MODE is ", authMode) - optAuth := registry[authMode] - if optAuth == nil { + authenticator, ok := registry[authMode] + if !ok { return nil, fmt.Errorf("Unrecognized auth_mode: %s", authMode) } - return optAuth.Validate(auth) + return authenticator.Authenticate(m) } diff --git a/opt_auth/db/db.go b/auth/db/db.go similarity index 71% rename from opt_auth/db/db.go rename to auth/db/db.go index f9b76d7f6..2e56cb1a9 100644 --- a/opt_auth/db/db.go +++ b/auth/db/db.go @@ -12,18 +12,21 @@ See the License for the specific language governing permissions and limitations under the License. */ + package db import ( + "github.com/vmware/harbor/auth" "github.com/vmware/harbor/dao" "github.com/vmware/harbor/models" - "github.com/vmware/harbor/opt_auth" ) -type DbAuth struct{} +// Auth implements Authenticator interface to authenticate user against DB. +type Auth struct{} -func (d *DbAuth) Validate(auth models.AuthModel) (*models.User, error) { - u, err := dao.LoginByDb(auth) +// Authenticate calls dao to authenticate user. +func (d *Auth) Authenticate(m models.AuthModel) (*models.User, error) { + u, err := dao.LoginByDb(m) if err != nil { return nil, err } @@ -31,5 +34,5 @@ func (d *DbAuth) Validate(auth models.AuthModel) (*models.User, error) { } func init() { - opt_auth.Register("db_auth", &DbAuth{}) + auth.Register("db_auth", &Auth{}) } diff --git a/opt_auth/ldap/ldap.go b/auth/ldap/ldap.go similarity index 71% rename from opt_auth/ldap/ldap.go rename to auth/ldap/ldap.go index 214ff764e..6316f5327 100644 --- a/opt_auth/ldap/ldap.go +++ b/auth/ldap/ldap.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package ldap import ( @@ -21,35 +22,38 @@ import ( "os" "strings" + "github.com/vmware/harbor/auth" "github.com/vmware/harbor/dao" "github.com/vmware/harbor/models" - "github.com/vmware/harbor/opt_auth" "github.com/astaxie/beego" "github.com/mqu/openldap" ) -type LdapAuth struct{} +// Auth implements Authenticator interface to authenticate against LDAP +type Auth struct{} -const META_CHARS = "&|!=~*<>()" +const metaChars = "&|!=~*<>()" -func (l *LdapAuth) Validate(auth models.AuthModel) (*models.User, error) { +// Authenticate checks user's credential agains LDAP based on basedn template and LDAP URL, +// if the check is successful a dummy record will be insert into DB, such that this user can +// be associated to other entities in the system. +func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) { - ldapUrl := os.Getenv("LDAP_URL") - if ldapUrl == "" { + ldapURL := os.Getenv("LDAP_URL") + if ldapURL == "" { return nil, errors.New("Can not get any available LDAP_URL.") } - beego.Debug("ldapUrl:", ldapUrl) + beego.Debug("ldapURL:", ldapURL) - p := auth.Principal - for _, c := range META_CHARS { + p := m.Principal + for _, c := range metaChars { if strings.ContainsRune(p, c) { - log.Printf("The principal contains meta char: %q", c) - return nil, nil + return nil, fmt.Errorf("the principal contains meta char: %q", c) } } - ldap, err := openldap.Initialize(ldapUrl) + ldap, err := openldap.Initialize(ldapURL) if err != nil { return nil, err } @@ -62,10 +66,10 @@ func (l *LdapAuth) Validate(auth models.AuthModel) (*models.User, error) { return nil, errors.New("Can not get any available LDAP_BASE_DN.") } - baseDn := fmt.Sprintf(ldapBaseDn, auth.Principal) + baseDn := fmt.Sprintf(ldapBaseDn, m.Principal) beego.Debug("baseDn:", baseDn) - err = ldap.Bind(baseDn, auth.Password) + err = ldap.Bind(baseDn, m.Password) if err != nil { return nil, err } @@ -108,19 +112,19 @@ func (l *LdapAuth) Validate(auth models.AuthModel) (*models.User, error) { if err != nil { return nil, err } - u.UserId = currentUser.UserId + u.UserID = currentUser.UserID } else { u.Password = "12345678AbC" u.Comment = "registered from LDAP." - userId, err := dao.Register(u) + userID, err := dao.Register(u) if err != nil { return nil, err } - u.UserId = int(userId) + u.UserID = int(userID) } return &u, nil } func init() { - opt_auth.Register("ldap_auth", &LdapAuth{}) + auth.Register("ldap_auth", &Auth{}) } diff --git a/controllers/base.go b/controllers/base.go index 9f9046b0f..836aa1382 100644 --- a/controllers/base.go +++ b/controllers/base.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package controllers import ( @@ -23,14 +24,17 @@ import ( "github.com/beego/i18n" ) +// CommonController handles request from UI that doesn't expect a page, such as /login /logout ... type CommonController struct { BaseController } +// Render returns nil. func (c *CommonController) Render() error { return nil } +// BaseController wraps common methods such as i18n support, forward, which can be leveraged by other UI render controllers. type BaseController struct { beego.Controller i18n.Locale @@ -42,14 +46,15 @@ type langType struct { } const ( - DEFAULT_LANG = "en-US" + defaultLang = "en-US" ) var supportLanguages map[string]langType +// Prepare extracts the language information from request and populate data for rendering templates. func (b *BaseController) Prepare() { - var lang string = "" + var lang string al := b.Ctx.Request.Header.Get("Accept-Language") if len(al) > 4 { @@ -60,7 +65,7 @@ func (b *BaseController) Prepare() { } if _, exist := supportLanguages[lang]; exist == false { //Check if support the request language. - lang = DEFAULT_LANG //Set default language if not supported. + lang = defaultLang //Set default language if not supported. } sessionLang := b.GetSession("lang") @@ -88,8 +93,8 @@ func (b *BaseController) Prepare() { b.Data["CurLang"] = curLang.Name b.Data["RestLangs"] = restLangs - sessionUserId := b.GetSession("userId") - if sessionUserId != nil { + sessionUserID := b.GetSession("userId") + if sessionUserID != nil { b.Data["Username"] = b.GetSession("username") } authMode := os.Getenv("AUTH_MODE") @@ -99,6 +104,7 @@ func (b *BaseController) Prepare() { b.Data["AuthMode"] = authMode } +// ForwardTo setup layout and template for content for a page. func (b *BaseController) ForwardTo(pageTitle string, pageName string) { b.Layout = "segment/base-layout.tpl" b.TplName = "segment/base-layout.tpl" diff --git a/controllers/item_detail.go b/controllers/item_detail.go index cf5214fcd..09774f3ff 100644 --- a/controllers/item_detail.go +++ b/controllers/item_detail.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package controllers import ( @@ -25,21 +26,24 @@ import ( "github.com/astaxie/beego" ) +// ItemDetailController handles requet to /registry/detail, which shows the detail of a project. type ItemDetailController struct { BaseController } +// Get will check if user has permission to view a certain project, if not user will be redirected to signin or his homepage. +// If the check is passed it renders the project detail page. func (idc *ItemDetailController) Get() { - projectId, _ := idc.GetInt64("project_id") + projectID, _ := idc.GetInt64("project_id") - if projectId <= 0 { - beego.Error("Invalid project id:", projectId) + if projectID <= 0 { + beego.Error("Invalid project id:", projectID) idc.Redirect("/signIn", http.StatusFound) return } - project, err := dao.GetProjectById(projectId) + project, err := dao.GetProjectByID(projectID) if err != nil { beego.Error("Error occurred in GetProjectById:", err) @@ -51,19 +55,19 @@ func (idc *ItemDetailController) Get() { return } - sessionUserId := idc.GetSession("userId") + sessionUserID := idc.GetSession("userId") - if project.Public != 1 && sessionUserId == nil { + if project.Public != 1 && sessionUserID == nil { idc.Redirect("/signIn?uri="+url.QueryEscape(idc.Ctx.Input.URI()), http.StatusFound) return } - if sessionUserId != nil { + if sessionUserID != nil { idc.Data["Username"] = idc.GetSession("username") - idc.Data["UserId"] = sessionUserId.(int) + idc.Data["UserId"] = sessionUserID.(int) - roleList, err := dao.GetUserProjectRoles(models.User{UserId: sessionUserId.(int)}, projectId) + roleList, err := dao.GetUserProjectRoles(models.User{UserID: sessionUserID.(int)}, projectID) if err != nil { beego.Error("Error occurred in GetUserProjectRoles:", err) idc.CustomAbort(http.StatusInternalServerError, "Internal error.") @@ -75,14 +79,14 @@ func (idc *ItemDetailController) Get() { } if len(roleList) > 0 { - idc.Data["RoleId"] = roleList[0].RoleId + idc.Data["RoleId"] = roleList[0].RoleID } } - idc.Data["ProjectId"] = project.ProjectId + idc.Data["ProjectId"] = project.ProjectID idc.Data["ProjectName"] = project.Name idc.Data["OwnerName"] = project.OwnerName - idc.Data["OwnerId"] = project.OwnerId + idc.Data["OwnerId"] = project.OwnerID idc.Data["HarborRegUrl"] = os.Getenv("HARBOR_REG_URL") idc.Data["RepoName"] = idc.GetString("repo_name") diff --git a/controllers/login.go b/controllers/login.go index 0839e483b..ffeac3e5a 100644 --- a/controllers/login.go +++ b/controllers/login.go @@ -12,39 +12,45 @@ See the License for the specific language governing permissions and limitations under the License. */ + package controllers import ( "net/http" + "github.com/vmware/harbor/auth" "github.com/vmware/harbor/models" - "github.com/vmware/harbor/opt_auth" "github.com/astaxie/beego" ) +// IndexController handles request to / type IndexController struct { BaseController } +// Get renders the index page. func (c *IndexController) Get() { c.Data["Username"] = c.GetSession("username") c.ForwardTo("page_title_index", "index") } +// SignInController handles request to /signIn type SignInController struct { BaseController } +// Get renders Sign In page. func (sic *SignInController) Get() { sic.ForwardTo("page_title_sign_in", "sign-in") } +// Login handles login request from UI. func (c *CommonController) Login() { principal := c.GetString("principal") password := c.GetString("password") - user, err := opt_auth.Login(models.AuthModel{principal, password}) + user, err := auth.Login(models.AuthModel{principal, password}) if err != nil { beego.Error("Error occurred in UserLogin:", err) c.CustomAbort(http.StatusInternalServerError, "Internal error.") @@ -54,10 +60,11 @@ func (c *CommonController) Login() { c.CustomAbort(http.StatusUnauthorized, "") } - c.SetSession("userId", user.UserId) + c.SetSession("userId", user.UserID) c.SetSession("username", user.Username) } +// SwitchLanguage handles UI request to switch between different languages and re-render template based on language. func (c *CommonController) SwitchLanguage() { lang := c.GetString("lang") if lang == "en-US" || lang == "zh-CN" { @@ -67,6 +74,7 @@ func (c *CommonController) SwitchLanguage() { c.Redirect(c.Ctx.Request.Header.Get("Referer"), http.StatusFound) } +// Logout handles UI request to logout. func (c *CommonController) Logout() { c.DestroySession() } diff --git a/controllers/password.go b/controllers/password.go index 9b9ecc0af..9929c4f8f 100644 --- a/controllers/password.go +++ b/controllers/password.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package controllers import ( @@ -28,13 +29,15 @@ import ( "github.com/astaxie/beego" ) +// ChangePasswordController handles request to /changePassword type ChangePasswordController struct { BaseController } +// Get renders the page for user to change password. func (cpc *ChangePasswordController) Get() { - sessionUserId := cpc.GetSession("userId") - if sessionUserId == nil { + sessionUserID := cpc.GetSession("userId") + if sessionUserID == nil { cpc.Redirect("/signIn", http.StatusFound) return } @@ -42,148 +45,154 @@ func (cpc *ChangePasswordController) Get() { cpc.ForwardTo("page_title_change_password", "change-password") } -func (cpc *CommonController) UpdatePassword() { +// UpdatePassword handles UI request to update user's password, it only works when the auth mode is db_auth. +func (cc *CommonController) UpdatePassword() { - sessionUserId := cpc.GetSession("userId") + sessionUserID := cc.GetSession("userId") - if sessionUserId == nil { + if sessionUserID == nil { beego.Warning("User does not login.") - cpc.CustomAbort(http.StatusUnauthorized, "please_login_first") + cc.CustomAbort(http.StatusUnauthorized, "please_login_first") } - oldPassword := cpc.GetString("old_password") + oldPassword := cc.GetString("old_password") if oldPassword == "" { beego.Error("Old password is blank") - cpc.CustomAbort(http.StatusBadRequest, "Old password is blank") + cc.CustomAbort(http.StatusBadRequest, "Old password is blank") } - queryUser := models.User{UserId: sessionUserId.(int), Password: oldPassword} + queryUser := models.User{UserID: sessionUserID.(int), Password: oldPassword} user, err := dao.CheckUserPassword(queryUser) if err != nil { beego.Error("Error occurred in CheckUserPassword:", err) - cpc.CustomAbort(http.StatusInternalServerError, "Internal error.") + cc.CustomAbort(http.StatusInternalServerError, "Internal error.") } if user == nil { beego.Warning("Password input is not correct") - cpc.CustomAbort(http.StatusForbidden, "old_password_is_not_correct") + cc.CustomAbort(http.StatusForbidden, "old_password_is_not_correct") } - password := cpc.GetString("password") + password := cc.GetString("password") if password != "" { - updateUser := models.User{UserId: sessionUserId.(int), Password: password, Salt: user.Salt} + updateUser := models.User{UserID: sessionUserID.(int), Password: password, Salt: user.Salt} err = dao.ChangeUserPassword(updateUser, oldPassword) if err != nil { beego.Error("Error occurred in ChangeUserPassword:", err) - cpc.CustomAbort(http.StatusInternalServerError, "Internal error.") + cc.CustomAbort(http.StatusInternalServerError, "Internal error.") } } else { - cpc.CustomAbort(http.StatusBadRequest, "please_input_new_password") + cc.CustomAbort(http.StatusBadRequest, "please_input_new_password") } } +// ForgotPasswordController handles request to /forgotPassword type ForgotPasswordController struct { BaseController } -type MessageDetail struct { - Hint string - Url string - Uuid string -} - +// Get Renders the page for user to input Email to reset password. func (fpc *ForgotPasswordController) Get() { fpc.ForwardTo("page_title_forgot_password", "forgot-password") } -func (fpc *CommonController) SendEmail() { +type messageDetail struct { + Hint string + URL string + UUID string +} - email := fpc.GetString("email") +// SendEmail verifies the Email address and contact SMTP server to send reset password Email. +func (cc *CommonController) SendEmail() { + + email := cc.GetString("email") pass, _ := regexp.MatchString(`^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$`, email) if !pass { - fpc.CustomAbort(http.StatusBadRequest, "email_content_illegal") + cc.CustomAbort(http.StatusBadRequest, "email_content_illegal") } else { queryUser := models.User{Email: email} exist, err := dao.UserExists(queryUser, "email") if err != nil { beego.Error("Error occurred in UserExists:", err) - fpc.CustomAbort(http.StatusInternalServerError, "Internal error.") + cc.CustomAbort(http.StatusInternalServerError, "Internal error.") } if !exist { - fpc.CustomAbort(http.StatusNotFound, "email_does_not_exist") + cc.CustomAbort(http.StatusNotFound, "email_does_not_exist") } messageTemplate, err := template.ParseFiles("views/reset-password-mail.tpl") if err != nil { beego.Error("Parse email template file failed:", err) - fpc.CustomAbort(http.StatusInternalServerError, err.Error()) + cc.CustomAbort(http.StatusInternalServerError, err.Error()) } message := new(bytes.Buffer) - harborUrl := os.Getenv("HARBOR_URL") - if harborUrl == "" { - harborUrl = "localhost" + harborURL := os.Getenv("HARBOR_URL") + if harborURL == "" { + harborURL = "localhost" } uuid, err := dao.GenerateRandomString() if err != nil { beego.Error("Error occurred in GenerateRandomString:", err) - fpc.CustomAbort(http.StatusInternalServerError, "Internal error.") + cc.CustomAbort(http.StatusInternalServerError, "Internal error.") } - err = messageTemplate.Execute(message, MessageDetail{ - Hint: fpc.Tr("reset_email_hint"), - Url: harborUrl, - Uuid: uuid, + err = messageTemplate.Execute(message, messageDetail{ + Hint: cc.Tr("reset_email_hint"), + URL: harborURL, + UUID: uuid, }) if err != nil { beego.Error("message template error:", err) - fpc.CustomAbort(http.StatusInternalServerError, "internal_error") + cc.CustomAbort(http.StatusInternalServerError, "internal_error") } config, err := beego.AppConfig.GetSection("mail") if err != nil { beego.Error("Can not load app.conf:", err) - fpc.CustomAbort(http.StatusInternalServerError, "internal_error") + cc.CustomAbort(http.StatusInternalServerError, "internal_error") } mail := utils.Mail{ From: config["from"], To: []string{email}, - Subject: fpc.Tr("reset_email_subject"), + Subject: cc.Tr("reset_email_subject"), Message: message.String()} err = mail.SendMail() if err != nil { beego.Error("send email failed:", err) - fpc.CustomAbort(http.StatusInternalServerError, "send_email_failed") + cc.CustomAbort(http.StatusInternalServerError, "send_email_failed") } - user := models.User{ResetUuid: uuid, Email: email} - dao.UpdateUserResetUuid(user) + user := models.User{ResetUUID: uuid, Email: email} + dao.UpdateUserResetUUID(user) } } +// ResetPasswordController handles request to /resetPassword type ResetPasswordController struct { BaseController } +// Get checks if reset_uuid in the reset link is valid and render the result page for user to reset password. func (rpc *ResetPasswordController) Get() { - resetUuid := rpc.GetString("reset_uuid") - if resetUuid == "" { + resetUUID := rpc.GetString("reset_uuid") + if resetUUID == "" { beego.Error("Reset uuid is blank.") rpc.Redirect("/", http.StatusFound) return } - queryUser := models.User{ResetUuid: resetUuid} + queryUser := models.User{ResetUUID: resetUUID} user, err := dao.GetUser(queryUser) if err != nil { beego.Error("Error occurred in GetUser:", err) @@ -191,41 +200,42 @@ func (rpc *ResetPasswordController) Get() { } if user != nil { - rpc.Data["ResetUuid"] = user.ResetUuid + rpc.Data["ResetUuid"] = user.ResetUUID rpc.ForwardTo("page_title_reset_password", "reset-password") } else { rpc.Redirect("/", http.StatusFound) } } -func (rpc *CommonController) ResetPassword() { +// ResetPassword handles request from the reset page and reset password +func (cc *CommonController) ResetPassword() { - resetUuid := rpc.GetString("reset_uuid") - if resetUuid == "" { - rpc.CustomAbort(http.StatusBadRequest, "Reset uuid is blank.") + resetUUID := cc.GetString("reset_uuid") + if resetUUID == "" { + cc.CustomAbort(http.StatusBadRequest, "Reset uuid is blank.") } - queryUser := models.User{ResetUuid: resetUuid} + queryUser := models.User{ResetUUID: resetUUID} user, err := dao.GetUser(queryUser) if err != nil { beego.Error("Error occurred in GetUser:", err) - rpc.CustomAbort(http.StatusInternalServerError, "Internal error.") + cc.CustomAbort(http.StatusInternalServerError, "Internal error.") } if user == nil { beego.Error("User does not exist") - rpc.CustomAbort(http.StatusBadRequest, "User does not exist") + cc.CustomAbort(http.StatusBadRequest, "User does not exist") } - password := rpc.GetString("password") + password := cc.GetString("password") if password != "" { user.Password = password err = dao.ResetUserPassword(*user) if err != nil { beego.Error("Error occurred in ResetUserPassword:", err) - rpc.CustomAbort(http.StatusInternalServerError, "Internal error.") + cc.CustomAbort(http.StatusInternalServerError, "Internal error.") } } else { - rpc.CustomAbort(http.StatusBadRequest, "password_is_required") + cc.CustomAbort(http.StatusBadRequest, "password_is_required") } } diff --git a/controllers/project.go b/controllers/project.go index 4ba4ebff7..1125bcaa1 100644 --- a/controllers/project.go +++ b/controllers/project.go @@ -1,23 +1,26 @@ /* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - 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. + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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 controllers +// ProjectController handles request to /registry/project type ProjectController struct { BaseController } +// Get renders project page. func (p *ProjectController) Get() { p.Data["Username"] = p.GetSession("username") p.ForwardTo("page_title_project", "project") diff --git a/controllers/register.go b/controllers/register.go index 7f8553ae5..105819752 100644 --- a/controllers/register.go +++ b/controllers/register.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package controllers import ( @@ -25,10 +26,12 @@ import ( "github.com/astaxie/beego" ) +// RegisterController handles request to /register type RegisterController struct { BaseController } +// Get renders the Sign In page, it only works if the auth mode is set to db_auth func (rc *RegisterController) Get() { authMode := os.Getenv("AUTH_MODE") if authMode == "" || authMode == "db_auth" { @@ -38,6 +41,7 @@ func (rc *RegisterController) Get() { } } +// SignUp insert data into DB based on data in form. func (rc *CommonController) SignUp() { username := strings.TrimSpace(rc.GetString("username")) email := strings.TrimSpace(rc.GetString("email")) @@ -54,6 +58,7 @@ func (rc *CommonController) SignUp() { } } +// UserExists checks if user exists when user input value in sign in form. func (rc *CommonController) UserExists() { target := rc.GetString("target") value := rc.GetString("value") diff --git a/controllers/search.go b/controllers/search.go index ebd228fbd..8762a2975 100644 --- a/controllers/search.go +++ b/controllers/search.go @@ -1,23 +1,26 @@ /* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - 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. + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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 controllers +// SearchController handles request to /search type SearchController struct { BaseController } +// Get renders page for displaying search result. func (sc *SearchController) Get() { sc.Data["Username"] = sc.GetSession("username") sc.Data["QueryParam"] = sc.GetString("q") diff --git a/dao/access_log.go b/dao/access_log.go index 5860a30c3..721ac92cd 100644 --- a/dao/access_log.go +++ b/dao/access_log.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package dao import ( @@ -22,6 +23,7 @@ import ( "github.com/astaxie/beego/orm" ) +// AddAccessLog persists the access logs func AddAccessLog(accessLog models.AccessLog) error { o := orm.NewOrm() p, err := o.Raw(`insert into access_log @@ -32,11 +34,12 @@ func AddAccessLog(accessLog models.AccessLog) error { } defer p.Close() - _, err = p.Exec(accessLog.UserId, accessLog.ProjectId, accessLog.RepoName, accessLog.Guid, accessLog.Operation) + _, err = p.Exec(accessLog.UserID, accessLog.ProjectID, accessLog.RepoName, accessLog.GUID, accessLog.Operation) return err } +//GetAccessLogs gets access logs according to different conditions func GetAccessLogs(accessLog models.AccessLog) ([]models.AccessLog, error) { o := orm.NewOrm() @@ -44,11 +47,11 @@ func GetAccessLogs(accessLog models.AccessLog) ([]models.AccessLog, error) { from access_log a left join user u on a.user_id = u.user_id where a.project_id = ? ` queryParam := make([]interface{}, 1) - queryParam = append(queryParam, accessLog.ProjectId) + queryParam = append(queryParam, accessLog.ProjectID) - if accessLog.UserId != 0 { + if accessLog.UserID != 0 { sql += ` and a.user_id = ? ` - queryParam = append(queryParam, accessLog.UserId) + queryParam = append(queryParam, accessLog.UserID) } if accessLog.Operation != "" { sql += ` and a.operation = ? ` @@ -92,6 +95,7 @@ func GetAccessLogs(accessLog models.AccessLog) ([]models.AccessLog, error) { return accessLogList, nil } +// AccessLog ... func AccessLog(username, projectName, repoName, action string) error { o := orm.NewOrm() sql := "insert into access_log (user_id, project_id, repo_name, operation, op_time) " + diff --git a/dao/base.go b/dao/base.go index 90ba82143..dee02370a 100644 --- a/dao/base.go +++ b/dao/base.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package dao import ( @@ -22,12 +23,12 @@ import ( "strings" "time" - "github.com/astaxie/beego" "github.com/astaxie/beego/orm" - _ "github.com/go-sql-driver/mysql" + _ "github.com/go-sql-driver/mysql" //register mysql driver ) -const NON_EXIST_USER_ID = 0 +// NonExistUserID : if a user does not exist, the ID of the user will be 0. +const NonExistUserID = 0 func isIllegalLength(s string, min int, max int) bool { if min == -1 { @@ -48,6 +49,7 @@ func isContainIllegalChar(s string, illegalChar []string) bool { return false } +// GenerateRandomString generates a random string func GenerateRandomString() (string, error) { o := orm.NewOrm() var uuid string @@ -59,6 +61,7 @@ func GenerateRandomString() (string, error) { } +//InitDB initializes the database func InitDB() { orm.RegisterDriver("mysql", orm.DRMySQL) addr := os.Getenv("MYSQL_HOST") @@ -74,26 +77,7 @@ func InitDB() { password = os.Getenv("MYSQL_PWD") } - var flag bool = true - if addr == "" { - beego.Error("Unset env of MYSQL_HOST") - flag = false - } else if port == "" { - beego.Error("Unset env of MYSQL_PORT_3306_TCP_PORT") - flag = false - } else if username == "" { - beego.Error("Unset env of MYSQL_USR") - flag = false - } else if password == "" { - beego.Error("Unset env of MYSQL_PWD") - flag = false - } - - if !flag { - os.Exit(1) - } - - db_str := username + ":" + password + "@tcp(" + addr + ":" + port + ")/registry" + dbStr := username + ":" + password + "@tcp(" + addr + ":" + port + ")/registry" ch := make(chan int, 1) go func() { var err error @@ -114,7 +98,7 @@ func InitDB() { case <-time.After(60 * time.Second): panic("Failed to connect to DB after 60 seconds") } - err := orm.RegisterDataBase("default", "mysql", db_str) + err := orm.RegisterDataBase("default", "mysql", dbStr) if err != nil { panic(err) } diff --git a/dao/dao_test.go b/dao/dao_test.go index 8d08903ac..1bc1e1f0f 100644 --- a/dao/dao_test.go +++ b/dao/dao_test.go @@ -12,11 +12,11 @@ See the License for the specific language governing permissions and limitations under the License. */ + package dao import ( "fmt" - // "fmt" "log" "os" "testing" @@ -83,15 +83,15 @@ func clearUp(username string) { o.Commit() } -const USERNAME string = "Tester01" -const PROJECT_NAME string = "test_project" -const SYS_ADMIN int = 1 -const PROJECT_ADMIN int = 2 -const DEVELOPER int = 3 -const GUEST int = 4 +const username string = "Tester01" +const projectName string = "test_project" +const SysAdmin int = 1 +const projectAdmin int = 2 +const developer int = 3 +const guest int = 4 -const PUBLICITY_ON = 1 -const PUBLICITY_OFF = 0 +const publicityOn = 1 +const publicityOff = 0 func TestMain(m *testing.M) { @@ -108,9 +108,6 @@ func TestMain(m *testing.M) { log.Fatalf("environment variable DB_PORT is not set") } dbPassword := os.Getenv("DB_PWD") - if len(dbPassword) == 0 { - log.Fatalf("environment variable DB_PWD is not set") - } fmt.Printf("DB_HOST: %s, DB_USR: %s, DB_PORT: %s, DB_PWD: %s\n", dbHost, dbUser, dbPort, dbPassword) @@ -120,7 +117,7 @@ func TestMain(m *testing.M) { os.Setenv("MYSQL_PWD", dbPassword) os.Setenv("AUTH_MODE", "db_auth") InitDB() - clearUp(USERNAME) + clearUp(username) os.Exit(m.Run()) } @@ -128,7 +125,7 @@ func TestMain(m *testing.M) { func TestRegister(t *testing.T) { user := models.User{ - Username: USERNAME, + Username: username, Email: "tester01@vmware.com", Password: "Abc12345", Realname: "tester01", @@ -142,15 +139,15 @@ func TestRegister(t *testing.T) { //Check if user registered successfully. queryUser := models.User{ - Username: USERNAME, + Username: username, } newUser, err := GetUser(queryUser) if err != nil { t.Errorf("Error occurred in GetUser: %v", err) } - if newUser.Username != USERNAME { - t.Errorf("Username does not match, expected: %s, actual: %s", USERNAME, newUser.Username) + if newUser.Username != username { + t.Errorf("Username does not match, expected: %s, actual: %s", username, newUser.Username) } if newUser.Email != "tester01@vmware.com" { t.Errorf("Email does not match, expected: %s, actual: %s", "tester01@vmware.com", newUser.Email) @@ -161,12 +158,12 @@ func TestUserExists(t *testing.T) { var exists bool var err error - exists, err = UserExists(models.User{Username: USERNAME}, "username") + exists, err = UserExists(models.User{Username: username}, "username") if err != nil { t.Errorf("Error occurred in UserExists: %v", err) } if !exists { - t.Errorf("User %s was inserted but does not exist", USERNAME) + t.Errorf("User %s was inserted but does not exist", username) } exists, err = UserExists(models.User{Email: "tester01@vmware.com"}, "email") @@ -188,7 +185,7 @@ func TestUserExists(t *testing.T) { func TestLoginByUserName(t *testing.T) { userQuery := models.User{ - Username: USERNAME, + Username: username, Password: "Abc12345", } @@ -200,8 +197,8 @@ func TestLoginByUserName(t *testing.T) { t.Errorf("No found for user logined by username and password: %v", userQuery) } - if loginUser.Username != USERNAME { - t.Errorf("User's username does not match after login, expected: %s, actual: %s", USERNAME, loginUser.Username) + if loginUser.Username != username { + t.Errorf("User's username does not match after login, expected: %s, actual: %s", username, loginUser.Username) } } @@ -219,8 +216,8 @@ func TestLoginByEmail(t *testing.T) { if loginUser == nil { t.Errorf("No found for user logined by email and password : %v", userQuery) } - if loginUser.Username != USERNAME { - t.Errorf("User's username does not match after login, expected: %s, actual: %s", USERNAME, loginUser.Username) + if loginUser.Username != username { + t.Errorf("User's username does not match after login, expected: %s, actual: %s", username, loginUser.Username) } } @@ -228,7 +225,7 @@ var currentUser *models.User func TestGetUser(t *testing.T) { queryUser := models.User{ - Username: USERNAME, + Username: username, } var err error currentUser, err = GetUser(queryUser) @@ -251,12 +248,12 @@ func TestListUsers(t *testing.T) { if len(users) != 1 { t.Errorf("Expect one user in list, but the acutal length is %d, the list: %+v", len(users), users) } - users2, err := ListUsers(models.User{Username: USERNAME}) + users2, err := ListUsers(models.User{Username: username}) if len(users2) != 1 { t.Errorf("Expect one user in list, but the acutal length is %d, the list: %+v", len(users), users) } - if users2[0].Username != USERNAME { - t.Errorf("The username in result list does not match, expected: %s, actual: %s", USERNAME, users2[0].Username) + if users2[0].Username != username { + t.Errorf("The username in result list does not match, expected: %s, actual: %s", username, users2[0].Username) } } @@ -266,12 +263,12 @@ func TestResetUserPassword(t *testing.T) { t.Errorf("Error occurred in GenerateRandomString: %v", err) } - err = UpdateUserResetUuid(models.User{ResetUuid: uuid, Email: currentUser.Email}) + err = UpdateUserResetUUID(models.User{ResetUUID: uuid, Email: currentUser.Email}) if err != nil { t.Errorf("Error occurred in UpdateUserResetUuid: %v", err) } - err = ResetUserPassword(models.User{UserId: currentUser.UserId, Password: "HarborTester12345", ResetUuid: uuid, Salt: currentUser.Salt}) + err = ResetUserPassword(models.User{UserID: currentUser.UserID, Password: "HarborTester12345", ResetUUID: uuid, Salt: currentUser.Salt}) if err != nil { t.Errorf("Error occurred in ResetUserPassword: %v", err) } @@ -281,13 +278,13 @@ func TestResetUserPassword(t *testing.T) { t.Errorf("Error occurred in LoginByDb: %v", err) } - if loginedUser.Username != USERNAME { - t.Errorf("The username returned by Login does not match, expected: %s, acutal: %s", USERNAME, loginedUser.Username) + if loginedUser.Username != username { + t.Errorf("The username returned by Login does not match, expected: %s, acutal: %s", username, loginedUser.Username) } } func TestChangeUserPassword(t *testing.T) { - err := ChangeUserPassword(models.User{UserId: currentUser.UserId, Password: "NewHarborTester12345", Salt: currentUser.Salt}) + err := ChangeUserPassword(models.User{UserID: currentUser.UserID, Password: "NewHarborTester12345", Salt: currentUser.Salt}) if err != nil { t.Errorf("Error occurred in ChangeUserPassword: %v", err) } @@ -297,13 +294,13 @@ func TestChangeUserPassword(t *testing.T) { t.Errorf("Error occurred in LoginByDb: %v", err) } - if loginedUser.Username != USERNAME { - t.Errorf("The username returned by Login does not match, expected: %s, acutal: %s", USERNAME, loginedUser.Username) + if loginedUser.Username != username { + t.Errorf("The username returned by Login does not match, expected: %s, acutal: %s", username, loginedUser.Username) } } func TestChangeUserPasswordWithOldPassword(t *testing.T) { - err := ChangeUserPassword(models.User{UserId: currentUser.UserId, Password: "NewerHarborTester12345", Salt: currentUser.Salt}, "NewHarborTester12345") + err := ChangeUserPassword(models.User{UserID: currentUser.UserID, Password: "NewerHarborTester12345", Salt: currentUser.Salt}, "NewHarborTester12345") if err != nil { t.Errorf("Error occurred in ChangeUserPassword: %v", err) } @@ -311,13 +308,13 @@ func TestChangeUserPasswordWithOldPassword(t *testing.T) { if err != nil { t.Errorf("Error occurred in LoginByDb: %v", err) } - if loginedUser.Username != USERNAME { - t.Errorf("The username returned by Login does not match, expected: %s, acutal: %s", USERNAME, loginedUser.Username) + if loginedUser.Username != username { + t.Errorf("The username returned by Login does not match, expected: %s, acutal: %s", username, loginedUser.Username) } } func TestChangeUserPasswordWithIncorrectOldPassword(t *testing.T) { - err := ChangeUserPassword(models.User{UserId: currentUser.UserId, Password: "NNewerHarborTester12345", Salt: currentUser.Salt}, "WrongNewerHarborTester12345") + err := ChangeUserPassword(models.User{UserID: currentUser.UserID, Password: "NNewerHarborTester12345", Salt: currentUser.Salt}, "WrongNewerHarborTester12345") if err == nil { t.Errorf("Error does not occurred due to old password is incorrect.") } @@ -331,7 +328,7 @@ func TestChangeUserPasswordWithIncorrectOldPassword(t *testing.T) { } func TestQueryRelevantProjectsWhenNoProjectAdded(t *testing.T) { - projects, err := QueryRelevantProjects(currentUser.UserId) + projects, err := QueryRelevantProjects(currentUser.UserID) if err != nil { t.Errorf("Error occurred in QueryRelevantProjects: %v", err) } @@ -346,8 +343,8 @@ func TestQueryRelevantProjectsWhenNoProjectAdded(t *testing.T) { func TestAddProject(t *testing.T) { project := models.Project{ - OwnerId: currentUser.UserId, - Name: PROJECT_NAME, + OwnerID: currentUser.UserID, + Name: projectName, CreationTime: time.Now(), OwnerName: currentUser.Username, } @@ -357,12 +354,12 @@ func TestAddProject(t *testing.T) { t.Errorf("Error occurred in AddProject: %v", err) } - newProject, err := GetProjectByName(PROJECT_NAME) + newProject, err := GetProjectByName(projectName) if err != nil { t.Errorf("Error occurred in GetProjectByName: %v", err) } if newProject == nil { - t.Errorf("No project found queried by project name: %v", PROJECT_NAME) + t.Errorf("No project found queried by project name: %v", projectName) } } @@ -370,25 +367,25 @@ var currentProject *models.Project func TestGetProject(t *testing.T) { var err error - currentProject, err = GetProjectByName(PROJECT_NAME) + currentProject, err = GetProjectByName(projectName) if err != nil { t.Errorf("Error occurred in GetProjectByName: %v", err) } if currentProject == nil { - t.Errorf("No project found queried by project name: %v", PROJECT_NAME) + t.Errorf("No project found queried by project name: %v", projectName) } - if currentProject.Name != PROJECT_NAME { - t.Errorf("Project name does not match, expected: %s, actual: %s", PROJECT_NAME, currentProject.Name) + if currentProject.Name != projectName { + t.Errorf("Project name does not match, expected: %s, actual: %s", projectName, currentProject.Name) } } -func getProjectRole(projectId int64) []models.Role { +func getProjectRole(projectID int64) []models.Role { o := orm.NewOrm() var r []models.Role _, err := o.Raw(`select r.role_id, r.name from project_role pr left join role r on pr.role_id = r.role_id - where project_id = ?`, projectId).QueryRows(&r) + where project_id = ?`, projectID).QueryRows(&r) if err != nil { log.Printf("Error occurred in querying project_role: %v", err) } @@ -396,12 +393,12 @@ func getProjectRole(projectId int64) []models.Role { } func TestCheckProjectRoles(t *testing.T) { - r := getProjectRole(currentProject.ProjectId) + r := getProjectRole(currentProject.ProjectID) if len(r) != 3 { t.Errorf("The length of project roles is not 3") } - if r[1].RoleId != 3 { - t.Errorf("The role id does not match, expected: 3, acutal: %d", r[1].RoleId) + if r[1].RoleID != 3 { + t.Errorf("The role id does not match, expected: 3, acutal: %d", r[1].RoleID) } if r[1].Name != "developer" { t.Errorf("The name of role id: 3 should be developer, actual:%s", r[1].Name) @@ -410,8 +407,8 @@ func TestCheckProjectRoles(t *testing.T) { func TestGetAccessLog(t *testing.T) { queryAccessLog := models.AccessLog{ - UserId: currentUser.UserId, - ProjectId: currentProject.ProjectId, + UserID: currentUser.UserID, + ProjectID: currentProject.ProjectID, } accessLogs, err := GetAccessLogs(queryAccessLog) if err != nil { @@ -420,20 +417,20 @@ func TestGetAccessLog(t *testing.T) { if len(accessLogs) != 1 { t.Errorf("The length of accesslog list should be 1, actual: %d", len(accessLogs)) } - if accessLogs[0].RepoName != PROJECT_NAME+"/" { - t.Errorf("The project name does not match, expected: %s, actual: %s", PROJECT_NAME+"/", accessLogs[0].RepoName) + if accessLogs[0].RepoName != projectName+"/" { + t.Errorf("The project name does not match, expected: %s, actual: %s", projectName+"/", accessLogs[0].RepoName) } } func TestProjectExists(t *testing.T) { var exists bool var err error - exists, err = ProjectExists(currentProject.ProjectId) + 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) + t.Errorf("The project with id: %d, does not exist", currentProject.ProjectID) } exists, err = ProjectExists(currentProject.Name) if err != nil { @@ -445,8 +442,8 @@ func TestProjectExists(t *testing.T) { } func TestGetProjectById(t *testing.T) { - id := currentProject.ProjectId - p, err := GetProjectById(id) + id := currentProject.ProjectID + p, err := GetProjectByID(id) if err != nil { t.Errorf("Error in GetProjectById: %v, id: %d", err, id) } @@ -456,7 +453,7 @@ func TestGetProjectById(t *testing.T) { } func TestGetUserByProject(t *testing.T) { - pid := currentProject.ProjectId + pid := currentProject.ProjectID u1 := models.User{ Username: "%%Tester%%", } @@ -465,14 +462,14 @@ func TestGetUserByProject(t *testing.T) { } users, err := GetUserByProject(pid, u1) if err != nil { - t.Errorf("Error happened in GetUserByProject: %v, project Id: %d, user: %+v", u1) + t.Errorf("Error happened in GetUserByProject: %v, project Id: %d, user: %+v", err, pid, u1) } if len(users) != 1 { t.Errorf("unexpected length of user list, expected: 1, the users list: %+v", users) } users, err = GetUserByProject(pid, u2) if err != nil { - t.Errorf("Error happened in GetUserByProject: %v, project Id: %d, user: %+v", u2) + t.Errorf("Error happened in GetUserByProject: %v, project Id: %d, user: %+v", err, pid, u2) } if len(users) != 0 { t.Errorf("unexpected length of user list, expected: 0, the users list: %+v", users) @@ -481,44 +478,44 @@ func TestGetUserByProject(t *testing.T) { } func TestToggleProjectPublicity(t *testing.T) { - err := ToggleProjectPublicity(currentProject.ProjectId, PUBLICITY_ON) + err := ToggleProjectPublicity(currentProject.ProjectID, publicityOn) if err != nil { t.Errorf("Error occurred in ToggleProjectPublicity: %v", err) } - currentProject, err = GetProjectByName(PROJECT_NAME) + currentProject, err = GetProjectByName(projectName) if err != nil { t.Errorf("Error occurred in GetProjectByName: %v", err) } - if currentProject.Public != PUBLICITY_ON { - t.Errorf("project, id: %d, its publicity is not on", currentProject.ProjectId) + if currentProject.Public != publicityOn { + t.Errorf("project, id: %d, its publicity is not on", currentProject.ProjectID) } - err = ToggleProjectPublicity(currentProject.ProjectId, PUBLICITY_OFF) + err = ToggleProjectPublicity(currentProject.ProjectID, publicityOff) if err != nil { t.Errorf("Error occurred in ToggleProjectPublicity: %v", err) } - currentProject, err = GetProjectByName(PROJECT_NAME) + currentProject, err = GetProjectByName(projectName) if err != nil { t.Errorf("Error occurred in GetProjectByName: %v", err) } - if currentProject.Public != PUBLICITY_OFF { - t.Errorf("project, id: %d, its publicity is not off", currentProject.ProjectId) + if currentProject.Public != publicityOff { + t.Errorf("project, id: %d, its publicity is not off", currentProject.ProjectID) } } func TestIsProjectPublic(t *testing.T) { - if isPublic := IsProjectPublic(PROJECT_NAME); isPublic { - t.Errorf("project, id: %d, its publicity is not false after turning off", currentProject.ProjectId) + if isPublic := IsProjectPublic(projectName); isPublic { + t.Errorf("project, id: %d, its publicity is not false after turning off", currentProject.ProjectID) } } func TestQueryProject(t *testing.T) { query1 := models.Project{ - UserId: 1, + UserID: 1, } projects, err := QueryProject(query1) if err != nil { @@ -538,7 +535,7 @@ func TestQueryProject(t *testing.T) { t.Errorf("Expecting get 1 project, but actual: %d, the list: %+v", len(projects), projects) } query3 := models.Project{ - UserId: 9, + UserID: 9, } projects, err = QueryProject(query3) if err != nil { @@ -549,14 +546,14 @@ func TestQueryProject(t *testing.T) { } } -func getUserProjectRole(projectId int64, userId int) []models.Role { +func getUserProjectRole(projectID int64, userID int) []models.Role { o := orm.NewOrm() var r []models.Role _, err := o.Raw(`select r.role_id, r.name from user_project_role upr left join project_role pr on upr.pr_id = pr.pr_id left join role r on r.role_id = pr.role_id - where pr.project_id = ? and upr.user_id = ?`, projectId, userId).QueryRows(&r) + where pr.project_id = ? and upr.user_id = ?`, projectID, userID).QueryRows(&r) if err != nil { log.Fatalf("Error occurred in querying user_project_role: %v", err) } @@ -565,28 +562,28 @@ func getUserProjectRole(projectId int64, userId int) []models.Role { func TestGetUserProjectRoles(t *testing.T) { user := *currentUser - r, err := GetUserProjectRoles(user, currentProject.ProjectId) + r, err := GetUserProjectRoles(user, currentProject.ProjectID) if err != nil { - t.Errorf("Error happened in GetUserProjectRole: %v, user: %+v, project Id: %d", err, user, currentProject.ProjectId) + t.Errorf("Error happened in GetUserProjectRole: %v, user: %+v, project Id: %d", err, user, currentProject.ProjectID) } //Get the size of current user project role. if len(r) != 1 { - t.Errorf("The user, id: %d, should only have one role in project, id: %d, but actual: %d", currentUser.UserId, currentProject.ProjectId, len(r)) + t.Errorf("The user, id: %d, should only have one role in project, id: %d, but actual: %d", currentUser.UserID, currentProject.ProjectID, len(r)) } if r[0].Name != "projectAdmin" { t.Errorf("the expected rolename is: projectAdmin, actual: %s", r[0].Name) } - user.RoleId = 1 + user.RoleID = 1 - r, err = GetUserProjectRoles(user, currentProject.ProjectId) + r, err = GetUserProjectRoles(user, currentProject.ProjectID) if err != nil { - t.Errorf("Error happened in GetUserProjectRole: %v, user: %+v, project Id: %d", err, user, currentProject.ProjectId) + t.Errorf("Error happened in GetUserProjectRole: %v, user: %+v, project Id: %d", err, user, currentProject.ProjectID) } //Get the size of current user project role. if len(r) != 0 { - t.Errorf("The user, id: %d, should not have role id: 1 in project id: %d, actual role list: %v", currentUser.UserId, currentProject.ProjectId, r) + t.Errorf("The user, id: %d, should not have role id: 1 in project id: %d, actual role list: %v", currentUser.UserID, currentProject.ProjectID, r) } } @@ -601,43 +598,43 @@ func TestProjectPermission(t *testing.T) { } func TestQueryRelevantProjects(t *testing.T) { - projects, err := QueryRelevantProjects(currentUser.UserId) + projects, err := QueryRelevantProjects(currentUser.UserID) if err != nil { t.Errorf("Error occurred in QueryRelevantProjects: %v", err) } if len(projects) != 2 { t.Errorf("Expected length of relevant projects is 2, but actual: %d, the projects: %+v", len(projects), projects) } - if projects[1].Name != PROJECT_NAME { - t.Errorf("Expected project name in the list: %s, actual: %s", PROJECT_NAME, projects[1].Name) + if projects[1].Name != projectName { + t.Errorf("Expected project name in the list: %s, actual: %s", projectName, projects[1].Name) } } func TestAssignUserProjectRole(t *testing.T) { - err := AddUserProjectRole(currentUser.UserId, currentProject.ProjectId, DEVELOPER) + err := AddUserProjectRole(currentUser.UserID, currentProject.ProjectID, developer) if err != nil { t.Errorf("Error occurred in AddUserProjectRole: %v", err) } - r := getUserProjectRole(currentProject.ProjectId, currentUser.UserId) + r := getUserProjectRole(currentProject.ProjectID, currentUser.UserID) //Get the size of current user project role info. if len(r) != 2 { t.Errorf("Expected length of role list is 2, actual: %d", len(r)) } - if r[1].RoleId != 3 { - t.Errorf("Expected role id of the second role in list is 3, actual: %d", r[1].RoleId) + if r[1].RoleID != 3 { + t.Errorf("Expected role id of the second role in list is 3, actual: %d", r[1].RoleID) } } func TestDeleteUserProjectRole(t *testing.T) { - err := DeleteUserProjectRoles(currentUser.UserId, currentProject.ProjectId) + err := DeleteUserProjectRoles(currentUser.UserID, currentProject.ProjectID) if err != nil { t.Errorf("Error occurred in DeleteUserProjectRoles: %v", err) } - r := getUserProjectRole(currentProject.ProjectId, currentUser.UserId) + r := getUserProjectRole(currentProject.ProjectID, currentUser.UserID) //Get the size of current user project role. if len(r) != 0 { t.Errorf("Expected role list length is 0, actual: %d, role list: %+v", len(r), r) @@ -649,28 +646,28 @@ func TestToggleAdminRole(t *testing.T) { if err != nil { t.Errorf("Error in toggle ToggleUserAdmin role: %v, user: %+v", err, currentUser) } - isAdmin, err := IsAdminRole(currentUser.UserId) + isAdmin, err := IsAdminRole(currentUser.UserID) if err != nil { - t.Errorf("Error in IsAdminRole: %v, user id: %d", err, currentUser.UserId) + t.Errorf("Error in IsAdminRole: %v, user id: %d", err, currentUser.UserID) } if !isAdmin { - t.Errorf("User is not admin after toggled, user id: %d", currentUser.UserId) + t.Errorf("User is not admin after toggled, user id: %d", currentUser.UserID) } err = ToggleUserAdminRole(*currentUser) if err != nil { t.Errorf("Error in toggle ToggleUserAdmin role: %v, user: %+v", err, currentUser) } - isAdmin, err = IsAdminRole(currentUser.UserId) + isAdmin, err = IsAdminRole(currentUser.UserID) if err != nil { - t.Errorf("Error in IsAdminRole: %v, user id: %d", err, currentUser.UserId) + t.Errorf("Error in IsAdminRole: %v, user id: %d", err, currentUser.UserID) } if isAdmin { - t.Errorf("User is still admin after toggled, user id: %d", currentUser.UserId) + t.Errorf("User is still admin after toggled, user id: %d", currentUser.UserID) } } func TestDeleteUser(t *testing.T) { - err := DeleteUser(currentUser.UserId) + err := DeleteUser(currentUser.UserID) if err != nil { t.Errorf("Error occurred in DeleteUser: %v", err) } @@ -679,6 +676,6 @@ func TestDeleteUser(t *testing.T) { t.Errorf("Error occurred in GetUser: %v", err) } if user != nil { - t.Error("user is not nil after deletion, user: %+v", user) + t.Errorf("user is not nil after deletion, user: %+v", user) } } diff --git a/dao/item_detail.go b/dao/item_detail.go index 40d2ed8d9..f4f2cb558 100644 --- a/dao/item_detail.go +++ b/dao/item_detail.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package dao import ( @@ -20,7 +21,8 @@ import ( "github.com/astaxie/beego/orm" ) -func GetUserByProject(projectId int64, queryUser models.User) ([]models.User, error) { +// GetUserByProject gets all members of the project. +func GetUserByProject(projectID int64, queryUser models.User) ([]models.User, error) { o := orm.NewOrm() u := []models.User{} sql := `select @@ -35,7 +37,7 @@ func GetUserByProject(projectId int64, queryUser models.User) ([]models.User, er and pr.project_id = ? ` queryParam := make([]interface{}, 1) - queryParam = append(queryParam, projectId) + queryParam = append(queryParam, projectID) if queryUser.Username != "" { sql += " and u.username like ? " diff --git a/dao/project.go b/dao/project.go index 4e9adc051..9d4205057 100644 --- a/dao/project.go +++ b/dao/project.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package dao import ( @@ -26,6 +27,8 @@ import ( ) //TODO:transaction, return err + +// AddProject adds a project to the database along with project roles information and access log records. func AddProject(project models.Project) error { if isIllegalLength(project.Name, 4, 30) { @@ -42,46 +45,47 @@ func AddProject(project models.Project) error { return err } - r, err := p.Exec(project.OwnerId, project.Name, project.Deleted, project.Public) + r, err := p.Exec(project.OwnerID, project.Name, project.Deleted, project.Public) if err != nil { return err } - projectId, err := r.LastInsertId() + projectID, err := r.LastInsertId() if err != nil { return err } - projectAdminRole := models.ProjectRole{ProjectId: projectId, RoleId: models.PROJECTADMIN} + projectAdminRole := models.ProjectRole{ProjectID: projectID, RoleID: models.PROJECTADMIN} _, err = AddProjectRole(projectAdminRole) if err != nil { return err } - projectDeveloperRole := models.ProjectRole{ProjectId: projectId, RoleId: models.DEVELOPER} + projectDeveloperRole := models.ProjectRole{ProjectID: projectID, RoleID: models.DEVELOPER} _, err = AddProjectRole(projectDeveloperRole) if err != nil { return err } - projectGuestRole := models.ProjectRole{ProjectId: projectId, RoleId: models.GUEST} + projectGuestRole := models.ProjectRole{ProjectID: projectID, RoleID: models.GUEST} _, err = AddProjectRole(projectGuestRole) if err != nil { return err } //Add all project roles, after that when assigning a user to a project just update the upr table - err = AddUserProjectRole(project.OwnerId, projectId, models.PROJECTADMIN) + err = AddUserProjectRole(project.OwnerID, projectID, models.PROJECTADMIN) if err != nil { return err } - accessLog := models.AccessLog{UserId: project.OwnerId, ProjectId: projectId, RepoName: project.Name + "/", Guid: "N/A", Operation: "create", OpTime: time.Now()} + accessLog := models.AccessLog{UserID: project.OwnerID, ProjectID: projectID, RepoName: project.Name + "/", GUID: "N/A", Operation: "create", OpTime: time.Now()} err = AddAccessLog(accessLog) return err } +// IsProjectPublic ... func IsProjectPublic(projectName string) bool { project, err := GetProjectByName(projectName) if err != nil { @@ -94,7 +98,7 @@ func IsProjectPublic(projectName string) bool { return project.Public == 1 } -//Query the projects based on publicity and user, disregarding the names etc. +// QueryProject querys the projects based on publicity and user, disregarding the names etc. func QueryProject(query models.Project) ([]models.Project, error) { o := orm.NewOrm() @@ -111,10 +115,10 @@ func QueryProject(query models.Project) ([]models.Project, error) { if query.Public == 1 { sql += ` and p.public = ?` queryParam = append(queryParam, query.Public) - } else if isAdmin, _ := IsAdminRole(query.UserId); isAdmin == false { + } else if isAdmin, _ := IsAdminRole(query.UserID); isAdmin == false { sql += ` and (p.owner_id = ? or u.user_id = ?) ` - queryParam = append(queryParam, query.UserId) - queryParam = append(queryParam, query.UserId) + queryParam = append(queryParam, query.UserID) + queryParam = append(queryParam, query.UserID) } if query.Name != "" { @@ -133,21 +137,22 @@ func QueryProject(query models.Project) ([]models.Project, error) { return r, nil } -func ProjectExists(nameOrId interface{}) (bool, error) { +//ProjectExists returns whether the project exists according to its name of ID. +func ProjectExists(nameOrID interface{}) (bool, error) { o := orm.NewOrm() type dummy struct{} sql := `select project_id from project where deleted = 0 and ` - switch nameOrId.(type) { + switch nameOrID.(type) { case int64: sql += `project_id = ?` case string: sql += `name = ?` default: - return false, errors.New(fmt.Sprintf("Invalid nameOrId: %v", nameOrId)) + return false, fmt.Errorf("Invalid nameOrId: %v", nameOrID) } var d []dummy - num, err := o.Raw(sql, nameOrId).QueryRows(&d) + num, err := o.Raw(sql, nameOrID).QueryRows(&d) if err != nil { return false, err } @@ -155,13 +160,14 @@ func ProjectExists(nameOrId interface{}) (bool, error) { } -func GetProjectById(projectId int64) (*models.Project, error) { +// GetProjectByID ... +func GetProjectByID(projectID int64) (*models.Project, error) { o := orm.NewOrm() sql := `select p.project_id, p.name, u.username as owner_name, p.owner_id, p.creation_time, p.public from project p left join user u on p.owner_id = u.user_id where p.deleted = 0 and p.project_id = ?` queryParam := make([]interface{}, 1) - queryParam = append(queryParam, projectId) + queryParam = append(queryParam, projectID) p := []models.Project{} count, err := o.Raw(sql, queryParam).QueryRows(&p) @@ -175,6 +181,7 @@ func GetProjectById(projectId int64) (*models.Project, error) { } } +// GetProjectByName ... func GetProjectByName(projectName string) (*models.Project, error) { o := orm.NewOrm() var p []models.Project @@ -188,6 +195,7 @@ func GetProjectByName(projectName string) (*models.Project, error) { } } +// GetPermission gets roles that the user has according to the project. func GetPermission(username, projectName string) (string, error) { o := orm.NewOrm() @@ -209,21 +217,23 @@ func GetPermission(username, projectName string) (string, error) { } } -func ToggleProjectPublicity(projectId int64, publicity int) error { +// ToggleProjectPublicity toggles the publicity of the project. +func ToggleProjectPublicity(projectID int64, publicity int) error { o := orm.NewOrm() sql := "update project set public = ? where project_id = ?" - _, err := o.Raw(sql, publicity, projectId).Exec() + _, err := o.Raw(sql, publicity, projectID).Exec() return err } -func QueryRelevantProjects(userId int) ([]models.Project, error) { +// QueryRelevantProjects returns all projects that the user is a member of. +func QueryRelevantProjects(userID int) ([]models.Project, error) { o := orm.NewOrm() sql := `SELECT distinct p.project_id, p.name, p.public FROM registry.project p left join project_role pr on p.project_id = pr.project_id left join user_project_role upr on upr.pr_id = pr.pr_id where upr.user_id = ? or p.public = 1 and p.deleted = 0` var res []models.Project - _, err := o.Raw(sql, userId).QueryRows(&res) + _, err := o.Raw(sql, userID).QueryRows(&res) if err != nil { return nil, err } diff --git a/dao/project_role.go b/dao/project_role.go index bbc9217e0..dbf409fa0 100644 --- a/dao/project_role.go +++ b/dao/project_role.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package dao import ( @@ -20,6 +21,7 @@ import ( "github.com/astaxie/beego/orm" ) +// AddProjectRole ... func AddProjectRole(projectRole models.ProjectRole) (int64, error) { o := orm.NewOrm() p, err := o.Raw("insert into project_role (project_id, role_id) values (?, ?)").Prepare() @@ -27,7 +29,7 @@ func AddProjectRole(projectRole models.ProjectRole) (int64, error) { return 0, err } defer p.Close() - r, err := p.Exec(projectRole.ProjectId, projectRole.RoleId) + r, err := p.Exec(projectRole.ProjectID, projectRole.RoleID) if err != nil { return 0, err } @@ -35,16 +37,17 @@ func AddProjectRole(projectRole models.ProjectRole) (int64, error) { return id, err } -func AddUserProjectRole(userId int, projectId int64, roleId int) error { +// AddUserProjectRole inserts role information to table project_role and user_project_role. +func AddUserProjectRole(userID int, projectID int64, roleID int) error { o := orm.NewOrm() var pr []models.ProjectRole - var prId int + var prID int sql := `select pr.pr_id, pr.project_id, pr.role_id from project_role pr where pr.project_id = ? and pr.role_id = ?` - n, err := o.Raw(sql, projectId, roleId).QueryRows(&pr) + n, err := o.Raw(sql, projectID, roleID).QueryRows(&pr) if err != nil { return err } @@ -55,7 +58,7 @@ func AddUserProjectRole(userId int, projectId int64, roleId int) error { return err } defer p.Close() - r, err := p.Exec(projectId, roleId) + r, err := p.Exec(projectID, roleID) if err != nil { return err } @@ -63,20 +66,21 @@ func AddUserProjectRole(userId int, projectId int64, roleId int) error { if err != nil { return err } - prId = int(id) + prID = int(id) } else if n > 0 { - prId = pr[0].PrId + prID = pr[0].PrID } p, err := o.Raw("insert into user_project_role (user_id, pr_id) values (?, ?)").Prepare() if err != nil { return err } defer p.Close() - _, err = p.Exec(userId, prId) + _, err = p.Exec(userID, prID) return err } -func DeleteUserProjectRoles(userId int, projectId int64) error { +// DeleteUserProjectRoles ... +func DeleteUserProjectRoles(userID int, projectID int64) error { o := orm.NewOrm() sql := `delete from user_project_role where user_id = ? and pr_id in (select pr_id from project_role where project_id = ?)` @@ -84,6 +88,6 @@ func DeleteUserProjectRoles(userId int, projectId int64) error { if err != nil { return err } - _, err = p.Exec(userId, projectId) + _, err = p.Exec(userID, projectID) return err } diff --git a/dao/register.go b/dao/register.go index a8311ebf5..d589be4db 100644 --- a/dao/register.go +++ b/dao/register.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package dao import ( @@ -24,6 +25,7 @@ import ( "github.com/astaxie/beego/orm" ) +// Register is used for user to register, the password is encrypted before the record is inserted into database. func Register(user models.User) (int64, error) { err := validate(user) @@ -48,12 +50,12 @@ func Register(user models.User) (int64, error) { if err != nil { return 0, err } - userId, err := r.LastInsertId() + userID, err := r.LastInsertId() if err != nil { return 0, err } - return userId, nil + return userID, nil } func validate(user models.User) error { @@ -99,6 +101,7 @@ func validate(user models.User) error { return nil } +// UserExists returns whether a user exists according username or Email. func UserExists(user models.User, target string) (bool, error) { if user.Username == "" && user.Email == "" { diff --git a/dao/role.go b/dao/role.go index 2379b149b..47b9a9d7d 100644 --- a/dao/role.go +++ b/dao/role.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package dao import ( @@ -20,7 +21,8 @@ import ( "github.com/astaxie/beego/orm" ) -func GetUserProjectRoles(userQuery models.User, projectId int64) ([]models.Role, error) { +// GetUserProjectRoles returns roles that the user has according to the project. +func GetUserProjectRoles(userQuery models.User, projectID int64) ([]models.Role, error) { o := orm.NewOrm() @@ -32,15 +34,15 @@ func GetUserProjectRoles(userQuery models.User, projectId int64) ([]models.Role, where u.deleted = 0 and u.user_id = ? ` queryParam := make([]interface{}, 1) - queryParam = append(queryParam, userQuery.UserId) + queryParam = append(queryParam, userQuery.UserID) - if projectId > 0 { + if projectID > 0 { sql += ` and pr.project_id = ? ` - queryParam = append(queryParam, projectId) + queryParam = append(queryParam, projectID) } - if userQuery.RoleId > 0 { + if userQuery.RoleID > 0 { sql += ` and r.role_id = ? ` - queryParam = append(queryParam, userQuery.RoleId) + queryParam = append(queryParam, userQuery.RoleID) } var roleList []models.Role @@ -52,9 +54,10 @@ func GetUserProjectRoles(userQuery models.User, projectId int64) ([]models.Role, return roleList, nil } -func IsAdminRole(userId int) (bool, error) { +// IsAdminRole returns whether the user is admin. +func IsAdminRole(userID int) (bool, error) { //role_id == 1 means the user is system admin - userQuery := models.User{UserId: userId, RoleId: models.SYSADMIN} + userQuery := models.User{UserID: userID, RoleID: models.SYSADMIN} adminRoleList, err := GetUserProjectRoles(userQuery, 0) if err != nil { return false, err diff --git a/dao/user.go b/dao/user.go index a0a259cc5..10b3dbe15 100644 --- a/dao/user.go +++ b/dao/user.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package dao import ( @@ -25,6 +26,7 @@ import ( "github.com/astaxie/beego/orm" ) +// GetUser ... func GetUser(query models.User) (*models.User, error) { o := orm.NewOrm() @@ -38,9 +40,9 @@ func GetUser(query models.User) (*models.User, error) { from user u where deleted = 0 ` queryParam := make([]interface{}, 1) - if query.UserId != 0 { + if query.UserID != 0 { sql += ` and user_id = ? ` - queryParam = append(queryParam, query.UserId) + queryParam = append(queryParam, query.UserID) } if query.Username != "" { @@ -48,9 +50,9 @@ func GetUser(query models.User) (*models.User, error) { queryParam = append(queryParam, query.Username) } - if query.ResetUuid != "" { + if query.ResetUUID != "" { sql += ` and reset_uuid = ? ` - queryParam = append(queryParam, query.ResetUuid) + queryParam = append(queryParam, query.ResetUUID) } var u []models.User @@ -65,6 +67,7 @@ func GetUser(query models.User) (*models.User, error) { } } +// LoginByDb is used for user to login with database auth mode. func LoginByDb(auth models.AuthModel) (*models.User, error) { query := models.User{Username: auth.Principal, Email: auth.Principal} @@ -84,6 +87,7 @@ func LoginByDb(auth models.AuthModel) (*models.User, error) { } +// ListUsers lists all users according to different conditions. func ListUsers(query models.User) ([]models.User, error) { o := orm.NewOrm() u := []models.User{} @@ -106,15 +110,16 @@ func ListUsers(query models.User) ([]models.User, error) { return u, err } +// ToggleUserAdminRole gives a user admim role. func ToggleUserAdminRole(u models.User) error { - projectRole := models.ProjectRole{PrId: 1} //admin project role + projectRole := models.ProjectRole{PrID: 1} //admin project role o := orm.NewOrm() var pr []models.ProjectRole - n, err := o.Raw(`select user_id from user_project_role where user_id = ? and pr_id = ? `, u.UserId, projectRole.PrId).QueryRows(&pr) + n, err := o.Raw(`select user_id from user_project_role where user_id = ? and pr_id = ? `, u.UserID, projectRole.PrID).QueryRows(&pr) if err != nil { return err } @@ -131,20 +136,21 @@ func ToggleUserAdminRole(u models.User) error { return err } defer p.Close() - _, err = p.Exec(u.UserId, projectRole.PrId) + _, err = p.Exec(u.UserID, projectRole.PrID) return err } +// ChangeUserPassword ... func ChangeUserPassword(u models.User, oldPassword ...string) error { o := orm.NewOrm() var err error var r sql.Result if len(oldPassword) == 0 { //In some cases, it may no need to check old password, just as Linux change password policies. - _, err = o.Raw(`update user set password=?, salt=? where user_id=?`, utils.Encrypt(u.Password, u.Salt), u.Salt, u.UserId).Exec() + _, err = o.Raw(`update user set password=?, salt=? where user_id=?`, utils.Encrypt(u.Password, u.Salt), u.Salt, u.UserID).Exec() } else if len(oldPassword) == 1 { - r, err = o.Raw(`update user set password=?, salt=? where user_id=? and password = ?`, utils.Encrypt(u.Password, u.Salt), u.Salt, u.UserId, utils.Encrypt(oldPassword[0], u.Salt)).Exec() + r, err = o.Raw(`update user set password=?, salt=? where user_id=? and password = ?`, utils.Encrypt(u.Password, u.Salt), u.Salt, u.UserID, utils.Encrypt(oldPassword[0], u.Salt)).Exec() if err != nil { return err } @@ -161,9 +167,10 @@ func ChangeUserPassword(u models.User, oldPassword ...string) error { return err } +// ResetUserPassword ... func ResetUserPassword(u models.User) error { o := orm.NewOrm() - r, err := o.Raw(`update user set password=?, reset_uuid=? where reset_uuid=?`, utils.Encrypt(u.Password, u.Salt), "", u.ResetUuid).Exec() + r, err := o.Raw(`update user set password=?, reset_uuid=? where reset_uuid=?`, utils.Encrypt(u.Password, u.Salt), "", u.ResetUUID).Exec() if err != nil { return err } @@ -177,12 +184,14 @@ func ResetUserPassword(u models.User) error { return err } -func UpdateUserResetUuid(u models.User) error { +// UpdateUserResetUUID ... +func UpdateUserResetUUID(u models.User) error { o := orm.NewOrm() - _, err := o.Raw(`update user set reset_uuid=? where email=?`, u.ResetUuid, u.Email).Exec() + _, err := o.Raw(`update user set reset_uuid=? where email=?`, u.ResetUUID, u.Email).Exec() return err } +// CheckUserPassword checks whether the password is correct. func CheckUserPassword(query models.User) (*models.User, error) { currentUser, err := GetUser(query) @@ -199,10 +208,10 @@ func CheckUserPassword(query models.User) (*models.User, error) { queryParam := make([]interface{}, 1) - if query.UserId != 0 { + if query.UserID != 0 { sql += ` and password = ? and user_id = ?` queryParam = append(queryParam, utils.Encrypt(query.Password, currentUser.Salt)) - queryParam = append(queryParam, query.UserId) + queryParam = append(queryParam, query.UserID) } else { sql += ` and username = ? and password = ?` queryParam = append(queryParam, currentUser.Username) @@ -223,8 +232,9 @@ func CheckUserPassword(query models.User) (*models.User, error) { } } -func DeleteUser(userId int) error { +// DeleteUser ... +func DeleteUser(userID int) error { o := orm.NewOrm() - _, err := o.Raw(`update user set deleted = 1 where user_id = ?`, userId).Exec() + _, err := o.Raw(`update user set deleted = 1 where user_id = ?`, userID).Exec() return err } diff --git a/main.go b/main.go index 531372297..c69c445b1 100644 --- a/main.go +++ b/main.go @@ -12,17 +12,17 @@ See the License for the specific language governing permissions and limitations under the License. */ + package main import ( - "errors" "fmt" "log" + _ "github.com/vmware/harbor/auth/db" + _ "github.com/vmware/harbor/auth/ldap" "github.com/vmware/harbor/dao" "github.com/vmware/harbor/models" - _ "github.com/vmware/harbor/opt_auth/db" - _ "github.com/vmware/harbor/opt_auth/ldap" _ "github.com/vmware/harbor/routers" "os" @@ -31,19 +31,19 @@ import ( ) const ( - ADMIN_USER_ID = 1 + adminUserID = 1 ) -func updateInitPassword(userId int, password string) error { - queryUser := models.User{UserId: userId} +func updateInitPassword(userID int, password string) error { + queryUser := models.User{UserID: userID} user, err := dao.GetUser(queryUser) if err != nil { - log.Println("Failed to get user, userId:", userId) + log.Println("Failed to get user, userID:", userID) return err } if user == nil { - log.Printf("User id: %d does not exist.", userId) - return errors.New(fmt.Sprintf("User id: %s does not exist.", userId)) + log.Printf("User id: %d does not exist.", userID) + return fmt.Errorf("User id: %d does not exist.", userID) } else if user.Salt == "" { salt, err := dao.GenerateRandomString() if err != nil { @@ -54,12 +54,12 @@ func updateInitPassword(userId int, password string) error { user.Password = password err = dao.ChangeUserPassword(*user) if err != nil { - log.Printf("Failed to update user encrypted password, userId: %d, err: %v", userId, err) + log.Printf("Failed to update user encrypted password, userID: %d, err: %v", userID, err) return err } - log.Printf("User id: %d updated its encypted password successfully.", userId) + log.Printf("User id: %d updated its encypted password successfully.", userID) } else { - log.Printf("User id: %d already has its encrypted password.", userId) + log.Printf("User id: %d already has its encrypted password.", userID) } return nil } @@ -68,6 +68,6 @@ func main() { beego.BConfig.WebConfig.Session.SessionOn = true dao.InitDB() - updateInitPassword(ADMIN_USER_ID, os.Getenv("HARBOR_ADMIN_PASSWORD")) + updateInitPassword(adminUserID, os.Getenv("HARBOR_ADMIN_PASSWORD")) beego.Run() } diff --git a/models/access_log.go b/models/access_log.go index a167d70de..150f3b588 100644 --- a/models/access_log.go +++ b/models/access_log.go @@ -12,20 +12,22 @@ 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 - UserId int - ProjectId int64 - RepoName string - Guid string - Operation string - OpTime time.Time + LogID int `orm:"column(log_id)" json:"LogId"` + UserID int `orm:"column(user_id)" json:"UserId"` + ProjectID int64 `orm:"column(project_id)" json:"ProjectId"` + RepoName string `orm:"column(repo_name)"` + GUID string `orm:"column(GUID)" json:"Guid"` + Operation string `orm:"column(operation)"` + OpTime time.Time `orm:"column(op_time)"` Username string Keywords string diff --git a/models/auth_model.go b/models/auth_model.go index e3ad85314..7878da98d 100644 --- a/models/auth_model.go +++ b/models/auth_model.go @@ -1,19 +1,21 @@ /* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - 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. + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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 +// AuthModel holds information used to authenticate. type AuthModel struct { Principal string Password string diff --git a/models/notification.go b/models/notification.go index 853d80bd0..0f608e2c6 100644 --- a/models/notification.go +++ b/models/notification.go @@ -1,29 +1,32 @@ /* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - 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. + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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" ) +// Notification holds all events. type Notification struct { Events []Event } +// Event holds the details of a event. type Event struct { - Id string + ID string `json:"Id"` TimeStamp time.Time Action string Target *Target @@ -31,19 +34,22 @@ type Event struct { Actor *Actor } +// Target holds information about the target of a event. type Target struct { MediaType string Digest string Repository string - Url string + URL string `json:"Url"` } +// Actor holds information about actor. type Actor struct { Name string } +// Request holds information about a request. type Request struct { - Id string + ID string `json:"Id"` Method string UserAgent string } diff --git a/models/project.go b/models/project.go index 126ee34f4..1b3bb92a2 100644 --- a/models/project.go +++ b/models/project.go @@ -1,33 +1,35 @@ /* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - 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. + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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" ) +// Project holds the details of a project. type Project struct { - ProjectId int64 - OwnerId int - Name string - CreationTime time.Time + ProjectID int64 `orm:"column(project_id)" json:"ProjectId"` + OwnerID int `orm:"column(owner_id)" json:"OwnerId"` + Name string `orm:"column(name)"` + CreationTime time.Time `orm:"column(creation_time)"` CreationTimeStr string - Deleted int - UserId int + Deleted int `orm:"column(deleted)"` + UserID int `json:"UserId"` OwnerName string - Public int + Public int `orm:"column(public)"` //This field does not have correspondent column in DB, this is just for UI to disable button Togglable bool } diff --git a/models/project_role.go b/models/project_role.go index dd70feacf..97049abf7 100644 --- a/models/project_role.go +++ b/models/project_role.go @@ -1,21 +1,23 @@ /* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - 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. + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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 +// ProjectRole holds information about the relationship of project and role. type ProjectRole struct { - PrId int - ProjectId int64 - RoleId int + PrID int `orm:"column(pr_id)" json:"PrId"` + ProjectID int64 `orm:"column(project_id)" json:"ProjectId"` + RoleID int `orm:"column(role_id)" json:"RoleId"` } diff --git a/models/repo.go b/models/repo.go index a247f0012..1692c645d 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1,25 +1,21 @@ /* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - 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. + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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 -type V1Repo struct { - NumResults int - Query string - Results []RepoItem -} - +// Repo holds information about repositories. type Repo struct { - Repositories []string `json:repositories` + Repositories []string `json:"repositories"` } diff --git a/models/repo_item.go b/models/repo_item.go index 6315893b8..d3febf0f9 100644 --- a/models/repo_item.go +++ b/models/repo_item.go @@ -1,32 +1,34 @@ /* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - 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. + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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" ) +// RepoItem holds manifest of an image. type RepoItem struct { - Id string `json:"Id"` - Parent string `json:"Parent"` - Created time.Time `json:"Created"` - CreatedStr string `json:"CreatedStr"` - DurationDays string `json:"Duration Days"` - Author string `json:"Author"` - Architecture string `json:"Architecture"` - Docker_version string `json:"Docker Version"` - Os string `json:"OS"` + ID string `json:"Id"` + Parent string `json:"Parent"` + Created time.Time `json:"Created"` + CreatedStr string `json:"CreatedStr"` + DurationDays string `json:"Duration Days"` + Author string `json:"Author"` + Architecture string `json:"Architecture"` + DockerVersion string `json:"Docker Version"` + Os string `json:"OS"` //Size int `json:"Size"` } diff --git a/models/role.go b/models/role.go index 847fb3347..693feeaca 100644 --- a/models/role.go +++ b/models/role.go @@ -1,28 +1,34 @@ /* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - 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. + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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 const ( - SYSADMIN = 1 + //SYSADMIN system administrator + SYSADMIN = 1 + //PROJECTADMIN project administrator PROJECTADMIN = 2 - DEVELOPER = 3 - GUEST = 4 + //DEVELOPER developer + DEVELOPER = 3 + //GUEST guest + GUEST = 4 ) +// Role holds the details of a role. type Role struct { - RoleId int `json:"role_id"` - RoleCode string `json:"role_code"` - Name string `json:"role_name"` + RoleID int `json:"role_id" orm:"column(role_id)"` + RoleCode string `json:"role_code" orm:"column(role_code)"` + Name string `json:"role_name" orm:"column(name)"` } diff --git a/models/tag.go b/models/tag.go index f9bc25fb8..386bea7ff 100644 --- a/models/tag.go +++ b/models/tag.go @@ -1,20 +1,22 @@ /* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - 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. + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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 +// Tag holds information about a tag. type Tag struct { - Version string `json:version` - ImageId string `json:image_id` + Version string `json:"version"` + ImageID string `json:"image_id"` } diff --git a/models/user.go b/models/user.go index 666723dee..e89d74b4b 100644 --- a/models/user.go +++ b/models/user.go @@ -1,31 +1,33 @@ /* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - 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. + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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 +// User holds the details of a user. type User struct { - UserId int - Username string - Email string - Password string - Realname string - Comment string - Deleted int + UserID int `orm:"column(user_id)" json:"UserId"` + Username string `orm:"column(username)"` + Email string `orm:"column(email)"` + Password string `orm:"column(password)"` + Realname string `orm:"column(realname)"` + Comment string `orm:"column(comment)"` + Deleted int `orm:"column(deleted)"` Rolename string - RoleId int + RoleID int `json:"RoleId"` RoleList []Role HasAdminRole int - ResetUuid string - Salt string + ResetUUID string `orm:"column(reset_uuid)" json:"ResetUuid"` + Salt string `orm:"column(salt)"` } diff --git a/models/user_project_role.go b/models/user_project_role.go index fcd5b1344..acd779d56 100644 --- a/models/user_project_role.go +++ b/models/user_project_role.go @@ -1,21 +1,23 @@ /* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - 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. + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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 +// UserProjectRole holds information about relationship of user, project and role. type UserProjectRole struct { - UprId int - UserId int - PrId int64 + UprID int `orm:"column(upr_id)" json:"UprId"` + UserID int `orm:"column(user_id)" json:"UserId"` + PrID int64 `orm:"column(pr_id)" json:"PrId"` } diff --git a/routers/router.go b/routers/router.go index e4b072054..764a3c7e6 100644 --- a/routers/router.go +++ b/routers/router.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package routers import ( @@ -61,5 +62,5 @@ func init() { //external service that hosted on harbor process: beego.Router("/service/notifications", &service.NotificationHandler{}) - beego.Router("/service/token", &service.AuthController{}, "get:Auth") + beego.Router("/service/token", &service.TokenHandler{}) } diff --git a/service/notification.go b/service/notification.go index 3419fb8ea..745dc56fb 100644 --- a/service/notification.go +++ b/service/notification.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package service import ( @@ -25,12 +26,14 @@ import ( "github.com/astaxie/beego" ) +// NotificationHandler handles request on /service/notifications/, which listens to registry's events. type NotificationHandler struct { beego.Controller } -const MEDIA_TYPE_MANIFEST = "application/vnd.docker.distribution.manifest.v1+json" +const mediaTypeManifest = "application/vnd.docker.distribution.manifest.v1+json" +// Post handles POST request, and records audit log or refreshes cache based on event. func (n *NotificationHandler) Post() { var notification models.Notification // log.Printf("Notification Handler triggered!\n") @@ -43,7 +46,7 @@ func (n *NotificationHandler) Post() { } var username, action, repo, project string for _, e := range notification.Events { - if e.Target.MediaType == MEDIA_TYPE_MANIFEST && strings.HasPrefix(e.Request.UserAgent, "docker") { + if e.Target.MediaType == mediaTypeManifest && strings.HasPrefix(e.Request.UserAgent, "docker") { username = e.Actor.Name action = e.Action repo = e.Target.Repository @@ -67,6 +70,7 @@ func (n *NotificationHandler) Post() { } +// Render returns nil as it won't render any template. func (n *NotificationHandler) Render() error { return nil } diff --git a/service/auth.go b/service/token.go similarity index 78% rename from service/auth.go rename to service/token.go index bfa3dfd68..62dfbbc29 100644 --- a/service/auth.go +++ b/service/token.go @@ -12,14 +12,15 @@ See the License for the specific language governing permissions and limitations under the License. */ + package service import ( "log" "net/http" + "github.com/vmware/harbor/auth" "github.com/vmware/harbor/models" - "github.com/vmware/harbor/opt_auth" svc_utils "github.com/vmware/harbor/service/utils" "github.com/vmware/harbor/utils" @@ -27,12 +28,15 @@ import ( "github.com/docker/distribution/registry/auth/token" ) -type AuthController struct { +// TokenHandler handles request on /service/token, which is the auth provider for registry. +type TokenHandler struct { beego.Controller } -//handle request -func (a *AuthController) Auth() { +// Get handles GET request, it checks the http header for user credentials +// and parse service and scope based on docker registry v2 standard, +// checkes the permission agains local DB and generates jwt token. +func (a *TokenHandler) Get() { request := a.Ctx.Request @@ -56,7 +60,7 @@ func (a *AuthController) Auth() { a.serveToken(username, service, access) } -func (a *AuthController) serveToken(username, service string, access []*token.ResourceActions) { +func (a *TokenHandler) serveToken(username, service string, access []*token.ResourceActions) { writer := a.Ctx.ResponseWriter //create token rawToken, err := svc_utils.MakeToken(username, service, access) @@ -72,14 +76,14 @@ func (a *AuthController) serveToken(username, service string, access []*token.Re } func authenticate(principal, password string) bool { - user, err := opt_auth.Login(models.AuthModel{principal, password}) + user, err := auth.Login(models.AuthModel{principal, password}) if err != nil { log.Printf("Error occurred in UserLogin: %v", err) return false } if user == nil { return false - } else { - return true } + + return true } diff --git a/service/utils/auth_utils.go b/service/utils/auth_utils.go index 1d53d4d46..77ba8a738 100644 --- a/service/utils/auth_utils.go +++ b/service/utils/auth_utils.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package utils import ( @@ -36,6 +37,7 @@ const ( expiration = 5 //minute ) +// GetResourceActions ... func GetResourceActions(scope string) []*token.ResourceActions { var res []*token.ResourceActions if scope == "" { @@ -50,55 +52,54 @@ func GetResourceActions(scope string) []*token.ResourceActions { return res } -//Try to modify the action list in access based on permission -//determine if the request needs to be authenticated. -//for details see:https://github.com/docker/docker/issues/15640 +// FilterAccess modify the action list in access based on permission +// determine if the request needs to be authenticated. func FilterAccess(username string, authenticated bool, a *token.ResourceActions) { if a.Type == "registry" && a.Name == "catalog" { return - } else { - //clear action list to assign to new acess element after perm check. - a.Actions = []string{} - if a.Type == "repository" { - if strings.Contains(a.Name, "/") { //Only check the permission when the requested image has a namespace, i.e. project - projectName := a.Name[0:strings.LastIndex(a.Name, "/")] - var permission string - var err error - if authenticated { - if username == "admin" { - exist, err := dao.ProjectExists(projectName) - if err != nil { - log.Printf("Error occurred in CheckExistProject: %v", err) - return - } - if exist { - permission = "RW" - } else { - permission = "" - log.Printf("project %s does not exist, set empty permission for admin", projectName) - } + } + + //clear action list to assign to new acess element after perm check. + a.Actions = []string{} + if a.Type == "repository" { + if strings.Contains(a.Name, "/") { //Only check the permission when the requested image has a namespace, i.e. project + projectName := a.Name[0:strings.LastIndex(a.Name, "/")] + var permission string + var err error + if authenticated { + if username == "admin" { + exist, err := dao.ProjectExists(projectName) + if err != nil { + log.Printf("Error occurred in CheckExistProject: %v", err) + return + } + if exist { + permission = "RW" } else { - permission, err = dao.GetPermission(username, projectName) - if err != nil { - log.Printf("Error occurred in GetPermission: %v", err) - return - } + permission = "" + log.Printf("project %s does not exist, set empty permission for admin", projectName) + } + } else { + permission, err = dao.GetPermission(username, projectName) + if err != nil { + log.Printf("Error occurred in GetPermission: %v", err) + return } } - if strings.Contains(permission, "W") { - a.Actions = append(a.Actions, "push") - } - if strings.Contains(permission, "R") || dao.IsProjectPublic(projectName) { - a.Actions = append(a.Actions, "pull") - } + } + if strings.Contains(permission, "W") { + a.Actions = append(a.Actions, "push") + } + if strings.Contains(permission, "R") || dao.IsProjectPublic(projectName) { + a.Actions = append(a.Actions, "pull") } } - log.Printf("current access, type: %s, name:%s, actions:%v \n", a.Type, a.Name, a.Actions) } + log.Printf("current access, type: %s, name:%s, actions:%v \n", a.Type, a.Name, a.Actions) } -//For the UI process to call, so it won't establish a https connection from UI to proxy. +// GenTokenForUI is for the UI process to call, so it won't establish a https connection from UI to proxy. func GenTokenForUI(username, service, scope string) (string, error) { access := GetResourceActions(scope) for _, a := range access { @@ -107,6 +108,7 @@ func GenTokenForUI(username, service, scope string) (string, error) { return MakeToken(username, service, access) } +// MakeToken makes a valid jwt token based on parms. func MakeToken(username, service string, access []*token.ResourceActions) (string, error) { pk, err := libtrust.LoadKeyFile(privateKey) if err != nil { diff --git a/service/utils/cache.go b/service/utils/cache.go index cb3ae7548..7a0ca93a4 100644 --- a/service/utils/cache.go +++ b/service/utils/cache.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package utils import ( @@ -25,9 +26,10 @@ import ( "github.com/astaxie/beego/cache" ) +// Cache is the global cache in system. var Cache cache.Cache -const CATALOG string = "catalog" +const catalogKey string = "catalog" func init() { var err error @@ -37,8 +39,9 @@ func init() { } } +// RefreshCatalogCache calls registry's API to get repository list and write it to cache. func RefreshCatalogCache() error { - result, err := RegistryApiGet(BuildRegistryUrl("_catalog"), "") + result, err := RegistryAPIGet(BuildRegistryURL("_catalog"), "") if err != nil { return err } @@ -47,19 +50,20 @@ func RefreshCatalogCache() error { if err != nil { return err } - Cache.Put(CATALOG, repoResp.Repositories, 600*time.Second) + Cache.Put(catalogKey, repoResp.Repositories, 600*time.Second) return nil } +// GetRepoFromCache get repository list from cache, it refreshes the cache if it's empty. func GetRepoFromCache() ([]string, error) { - result := Cache.Get(CATALOG) + result := Cache.Get(catalogKey) if result == nil { err := RefreshCatalogCache() if err != nil { return nil, err } - cached := Cache.Get(CATALOG) + cached := Cache.Get(catalogKey) if cached != nil { return cached.([]string), nil } diff --git a/service/utils/registry_utils.go b/service/utils/registry_utils.go index aa5def9e6..b1aa272d1 100644 --- a/service/utils/registry_utils.go +++ b/service/utils/registry_utils.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package utils import ( @@ -24,7 +25,8 @@ import ( "strings" ) -func BuildRegistryUrl(segments ...string) string { +// BuildRegistryURL ... +func BuildRegistryURL(segments ...string) string { registryURL := os.Getenv("REGISTRY_URL") if registryURL == "" { registryURL = "http://localhost:5000" @@ -40,7 +42,9 @@ func BuildRegistryUrl(segments ...string) string { return url } -func RegistryApiGet(url, username string) ([]byte, error) { +// RegistryAPIGet triggers GET request to the URL which is the endpoint of registry and returns the response body. +// It will attach a valid jwt token to the request if registry requires. +func RegistryAPIGet(url, username string) ([]byte, error) { response, err := http.Get(url) if err != nil { return nil, err diff --git a/tests/utils_test.go b/tests/utils_test.go index 22109561a..c774335ea 100644 --- a/tests/utils_test.go +++ b/tests/utils_test.go @@ -12,4 +12,5 @@ See the License for the specific language governing permissions and limitations under the License. */ + package test diff --git a/utils/encrypt.go b/utils/encrypt.go index f97dc53db..2b85e8d97 100644 --- a/utils/encrypt.go +++ b/utils/encrypt.go @@ -1,17 +1,18 @@ /* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - 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. + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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 utils import ( @@ -21,6 +22,7 @@ import ( "golang.org/x/crypto/pbkdf2" ) +// Encrypt encrypts the content with salt func Encrypt(content string, salt string) string { return fmt.Sprintf("%x", pbkdf2.Key([]byte(content), []byte(salt), 4096, 16, sha1.New)) } diff --git a/utils/mail.go b/utils/mail.go index 8fc0c02f0..8dca9f413 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -1,17 +1,18 @@ /* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - 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. + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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 utils import ( @@ -23,12 +24,15 @@ import ( "github.com/astaxie/beego" ) +// Mail holds information about content of Email type Mail struct { From string To []string Subject string Message string } + +// MailConfig holds information about Email configurations type MailConfig struct { Identity string Host string @@ -39,6 +43,7 @@ type MailConfig struct { var mc MailConfig +// SendMail sends Email according to the configurations func (m Mail) SendMail() error { if mc.Host == "" { diff --git a/utils/registry_utils.go b/utils/registry_utils.go index 13f989a2e..ab16b3a4e 100644 --- a/utils/registry_utils.go +++ b/utils/registry_utils.go @@ -1,17 +1,18 @@ /* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - 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. + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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 utils import ( @@ -27,9 +28,10 @@ import ( "github.com/astaxie/beego" ) -const SESSION_COOKIE = "beegosessionID" +const sessionCookie = "beegosessionID" -func BuildRegistryUrl(segments ...string) string { +// BuildRegistryURL builds the URL of registry +func BuildRegistryURL(segments ...string) string { registryURL := os.Getenv("REGISTRY_URL") if registryURL == "" { registryURL = "http://localhost:5000" @@ -45,8 +47,9 @@ func BuildRegistryUrl(segments ...string) string { return url } -func HttpGet(url, sessionId, username, password string) ([]byte, error) { - response, err := http.Get(url) +// HTTPGet is used to call the API of registry. If a token is needed, it will get a token first. +func HTTPGet(URL, sessionID, username, password string) ([]byte, error) { + response, err := http.Get(URL) if err != nil { return nil, err } @@ -60,7 +63,7 @@ func HttpGet(url, sessionId, username, password string) ([]byte, error) { } else if response.StatusCode == http.StatusUnauthorized { authenticate := response.Header.Get("WWW-Authenticate") str := strings.Split(authenticate, " ")[1] - beego.Trace("url: " + url) + beego.Trace("url: " + URL) beego.Trace("Authentication Header: " + str) var realm string var service string @@ -72,7 +75,7 @@ func HttpGet(url, sessionId, username, password string) ([]byte, error) { } else if strings.Contains(s, "service") { service = s } else if strings.Contains(s, "scope") { - strings.HasSuffix(url, "v2/_catalog") + strings.HasSuffix(URL, "v2/_catalog") scope = s } } @@ -80,18 +83,18 @@ func HttpGet(url, sessionId, username, password string) ([]byte, error) { service = strings.Split(service, "\"")[1] scope = strings.Split(scope, "\"")[1] - authUrl := realm + "?service=" + service + "&scope=" + scope + authURL := realm + "?service=" + service + "&scope=" + scope //skip certificate check if token service is https. tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } client := &http.Client{Transport: tr} - request, err := http.NewRequest("GET", authUrl, nil) + request, err := http.NewRequest("GET", authURL, nil) if err != nil { return nil, err } - if len(sessionId) > 0 { - cookie := &http.Cookie{Name: SESSION_COOKIE, Value: sessionId, Path: "/"} + if len(sessionID) > 0 { + cookie := &http.Cookie{Name: sessionCookie, Value: sessionID, Path: "/"} request.AddCookie(cookie) } else { request.SetBasicAuth(username, password) @@ -109,7 +112,7 @@ func HttpGet(url, sessionId, username, password string) ([]byte, error) { if response.StatusCode == http.StatusOK { tt := make(map[string]string) json.Unmarshal(result, &tt) - request, err = http.NewRequest("GET", url, nil) + request, err = http.NewRequest("GET", URL, nil) if err != nil { return nil, err } @@ -136,9 +139,8 @@ func HttpGet(url, sessionId, username, password string) ([]byte, error) { defer response.Body.Close() return result, nil - } else { - return nil, errors.New(string(result)) } + return nil, errors.New(string(result)) } else { return nil, errors.New(string(result)) } diff --git a/utils/utils.go b/utils/utils.go index 96260311b..592236bb5 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ + package utils import ( @@ -23,10 +24,12 @@ import ( "github.com/astaxie/beego" ) +// Repository holds information about repository type Repository struct { Name string } +// ParseBasicAuth parses the basic authorization func ParseBasicAuth(authorization []string) (username, password string) { if authorization == nil || len(authorization) == 0 { beego.Debug("Authorization header is not set.") @@ -38,6 +41,7 @@ func ParseBasicAuth(authorization []string) (username, password string) { return pair[0], pair[1] } +// GetProject parses the repository and return the name of project. func (r *Repository) GetProject() string { if !strings.ContainsRune(r.Name, '/') { return "" @@ -45,18 +49,22 @@ func (r *Repository) GetProject() string { return r.Name[0:strings.LastIndex(r.Name, "/")] } +// ProjectSorter holds an array of projects type ProjectSorter struct { Projects []models.Project } +// Len returns the length of array in ProjectSorter func (ps *ProjectSorter) Len() int { return len(ps.Projects) } +// Less defines the comparison rules of project func (ps *ProjectSorter) Less(i, j int) bool { return ps.Projects[i].Name < ps.Projects[j].Name } +// Swap swaps the position of i and j func (ps *ProjectSorter) Swap(i, j int) { ps.Projects[i], ps.Projects[j] = ps.Projects[j], ps.Projects[i] }