Merge pull request #16 from reasonerjt/golint-fix

Golint fix
This commit is contained in:
reasonerjt 2016-02-26 18:58:43 +08:00
commit 38d6d50e23
49 changed files with 924 additions and 759 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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()

View File

@ -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 {

View File

@ -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")

View File

@ -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
}

View File

@ -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)
}

View File

@ -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{})
}

View File

@ -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{})
}

View File

@ -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"

View File

@ -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")

View File

@ -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()
}

View File

@ -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")
}
}

View File

@ -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")

View File

@ -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")

View File

@ -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")

View File

@ -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) " +

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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 ? "

View File

@ -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
}

View File

@ -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
}

View File

@ -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 == "" {

View File

@ -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

View File

@ -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
}

26
main.go
View File

@ -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()
}

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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"`
}

View File

@ -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"`
}

View File

@ -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"`
}

View File

@ -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)"`
}

View File

@ -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"`
}

View File

@ -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)"`
}

View File

@ -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"`
}

View File

@ -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{})
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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

View File

@ -12,4 +12,5 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
package test

View File

@ -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))
}

View File

@ -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 == "" {

View File

@ -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))
}

View File

@ -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]
}