mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-22 00:27:44 +01:00
Merge pull request #3 from reasonerjt/master
sync code from personal repo
This commit is contained in:
commit
2bdf12e2b4
@ -15,12 +15,10 @@ http {
|
||||
|
||||
upstream registry {
|
||||
server registry:5000;
|
||||
# check interval=2000 rise=1 fall=1 timeout=5000 type=tcp;
|
||||
}
|
||||
|
||||
upstream ui {
|
||||
server ui:80;
|
||||
# check interval=2000 rise=1 fall=1 timeout=5000 type=tcp;
|
||||
}
|
||||
|
||||
|
||||
|
@ -39,20 +39,20 @@ ui:
|
||||
- ./config/ui/app.conf:/etc/ui/app.conf
|
||||
- ./config/ui/private_key.pem:/etc/ui/private_key.pem
|
||||
links:
|
||||
- registry:registry
|
||||
- mysql:mysql
|
||||
- registry
|
||||
- mysql
|
||||
- log
|
||||
log_driver: "syslog"
|
||||
log_opt:
|
||||
syslog-address: "tcp://127.0.0.1:1514"
|
||||
syslog-tag: "ui"
|
||||
roxy:
|
||||
proxy:
|
||||
image: library/nginx:1.9
|
||||
volumes:
|
||||
- ./config/nginx/nginx.conf:/etc/nginx/nginx.conf
|
||||
links:
|
||||
- ui:ui
|
||||
- registry:registry
|
||||
- ui
|
||||
- registry
|
||||
- log
|
||||
ports:
|
||||
- 80:80
|
||||
|
@ -1,23 +1,18 @@
|
||||
FROM library/ubuntu:14.04
|
||||
|
||||
# run logrotate hourly
|
||||
RUN mv /etc/cron.daily/logrotate /etc/cron.hourly/
|
||||
# run logrotate hourly, disable imklog model, provides TCP/UDP syslog reception
|
||||
RUN mv /etc/cron.daily/logrotate /etc/cron.hourly/ \
|
||||
&& sed 's/$ModLoad imklog/#$ModLoad imklog/' -i /etc/rsyslog.conf \
|
||||
&& sed 's/$KLogPermitNonKernelFacility on/#$KLogPermitNonKernelFacility on/' -i /etc/rsyslog.conf \
|
||||
&& sed 's/#$ModLoad imudp/$ModLoad imudp/' -i /etc/rsyslog.conf \
|
||||
&& sed 's/#$UDPServerRun 514/$UDPServerRun 514/' -i /etc/rsyslog.conf \
|
||||
&& sed 's/#$ModLoad imtcp/$ModLoad imtcp/' -i /etc/rsyslog.conf \
|
||||
&& sed 's/#$InputTCPServerRun 514/$InputTCPServerRun 514/' -i /etc/rsyslog.conf \
|
||||
&& rm /etc/rsyslog.d/*
|
||||
|
||||
# logrotate configuration file for docker
|
||||
ADD logrotate_docker.conf /etc/logrotate.d/
|
||||
|
||||
#disable imklog model
|
||||
RUN sed 's/$ModLoad imklog/#$ModLoad imklog/' -i /etc/rsyslog.conf
|
||||
RUN sed 's/$KLogPermitNonKernelFacility on/#$KLogPermitNonKernelFacility on/' -i /etc/rsyslog.conf
|
||||
|
||||
# provides TCP/UDP syslog reception
|
||||
RUN sed 's/#$ModLoad imudp/$ModLoad imudp/' -i /etc/rsyslog.conf
|
||||
RUN sed 's/#$UDPServerRun 514/$UDPServerRun 514/' -i /etc/rsyslog.conf
|
||||
RUN sed 's/#$ModLoad imtcp/$ModLoad imtcp/' -i /etc/rsyslog.conf
|
||||
RUN sed 's/#$InputTCPServerRun 514/$InputTCPServerRun 514/' -i /etc/rsyslog.conf
|
||||
|
||||
RUN rm /etc/rsyslog.d/*
|
||||
|
||||
# rsyslog configuration file for docker
|
||||
ADD rsyslog_docker.conf /etc/rsyslog.d/
|
||||
|
||||
@ -26,3 +21,4 @@ VOLUME /var/log/docker/
|
||||
EXPOSE 514
|
||||
|
||||
CMD cron && chown -R syslog:syslog /var/log/docker/ && rsyslogd -n
|
||||
|
||||
|
@ -14,8 +14,8 @@ WORKDIR /go/src/github.com/vmware/harbor
|
||||
ENV GO15VENDOREXPERIMENT 1
|
||||
RUN go get -d github.com/docker/distribution \
|
||||
&& go get -d github.com/docker/libtrust \
|
||||
&& go get -d github.com/go-sql-driver/mysql
|
||||
RUN go install -v -a
|
||||
&& go get -d github.com/go-sql-driver/mysql \
|
||||
&& go install -v -a
|
||||
|
||||
ENV MYSQL_USR root \
|
||||
MYSQL_PWD root \
|
||||
@ -23,7 +23,6 @@ ENV MYSQL_USR root \
|
||||
MYSQL_PORT_3306_TCP_PORT 3306 \
|
||||
REGISTRY_URL localhost:5000
|
||||
|
||||
COPY conf /go/bin/conf
|
||||
COPY views /go/bin/views
|
||||
COPY static /go/bin/static
|
||||
|
||||
|
@ -9,7 +9,6 @@ ENV REGISTRY_URL localhost:5000
|
||||
RUN apt-get update -qqy && apt-get install -qqy libldap2-dev
|
||||
|
||||
ADD harbor /go/bin/harbor
|
||||
ADD conf /go/bin/conf
|
||||
ADD views /go/bin/views
|
||||
ADD static /go/bin/static
|
||||
|
||||
|
@ -40,7 +40,7 @@ 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)
|
||||
b.CustomAbort(400, "Invalid json request")
|
||||
b.CustomAbort(http.StatusBadRequest, "Invalid json request")
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,17 +49,17 @@ func (b *BaseAPI) ValidateUser() int {
|
||||
sessionUserId := b.GetSession("userId")
|
||||
if sessionUserId == nil {
|
||||
beego.Warning("No user id in session, canceling request")
|
||||
b.CustomAbort(401, "")
|
||||
b.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
userId := sessionUserId.(int)
|
||||
u, err := dao.GetUser(models.User{UserId: userId})
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUser:", err)
|
||||
b.CustomAbort(500, "Internal error.")
|
||||
b.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if u == nil {
|
||||
beego.Warning("User was deleted already, user id: ", userId, " canceling request.")
|
||||
b.CustomAbort(401, "")
|
||||
b.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
return userId
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ package api
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
@ -48,15 +49,15 @@ func (p *ProjectAPI) Prepare() {
|
||||
p.projectId, err = strconv.ParseInt(id_str, 10, 64)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing project id: %s, error: %v", id_str, err)
|
||||
p.CustomAbort(400, "invalid project id")
|
||||
p.CustomAbort(http.StatusBadRequest, "invalid project id")
|
||||
}
|
||||
exist, err := dao.ProjectExists(p.projectId)
|
||||
if err != nil {
|
||||
log.Printf("Error occurred in ProjectExists: %v", err)
|
||||
p.CustomAbort(500, "Internal error.")
|
||||
p.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if !exist {
|
||||
p.CustomAbort(404, fmt.Sprintf("project does not exist, id: %v", p.projectId))
|
||||
p.CustomAbort(http.StatusNotFound, fmt.Sprintf("project does not exist, id: %v", p.projectId))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -71,7 +72,7 @@ func (p *ProjectAPI) Post() {
|
||||
err := validateProjectReq(req)
|
||||
if err != nil {
|
||||
beego.Error("Invalid project request, error: ", err)
|
||||
p.RenderError(400, "Invalid request for creating project")
|
||||
p.RenderError(http.StatusBadRequest, "Invalid request for creating project")
|
||||
return
|
||||
}
|
||||
projectName := req.ProjectName
|
||||
@ -80,14 +81,14 @@ func (p *ProjectAPI) Post() {
|
||||
beego.Error("Error happened checking project existence in db:", err, ", project name:", projectName)
|
||||
}
|
||||
if exist {
|
||||
p.RenderError(409, "")
|
||||
p.RenderError(http.StatusConflict, "")
|
||||
return
|
||||
}
|
||||
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)
|
||||
p.RenderError(500, "Failed to add project")
|
||||
p.RenderError(http.StatusInternalServerError, "Failed to add project")
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,11 +97,11 @@ func (p *ProjectAPI) Head() {
|
||||
result, err := dao.ProjectExists(projectName)
|
||||
if err != nil {
|
||||
beego.Error("Error while communicating with DB: ", err)
|
||||
p.RenderError(500, "Error while communicating with DB")
|
||||
p.RenderError(http.StatusInternalServerError, "Error while communicating with DB")
|
||||
return
|
||||
}
|
||||
if !result {
|
||||
p.RenderError(404, "")
|
||||
p.RenderError(http.StatusNotFound, "")
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -117,7 +118,7 @@ func (p *ProjectAPI) Get() {
|
||||
projectList, err := dao.QueryProject(queryProject)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in QueryProject:", err)
|
||||
p.CustomAbort(500, "Internal error.")
|
||||
p.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
for i := 0; i < len(projectList); i++ {
|
||||
if isProjectAdmin(p.userId, projectList[i].ProjectId) {
|
||||
@ -135,7 +136,7 @@ func (p *ProjectAPI) Put() {
|
||||
projectId, err := strconv.ParseInt(p.Ctx.Input.Param(":id"), 10, 64)
|
||||
if err != nil {
|
||||
beego.Error("Error parsing project id:", projectId, ", error: ", err)
|
||||
p.RenderError(400, "invalid project id")
|
||||
p.RenderError(http.StatusBadRequest, "invalid project id")
|
||||
return
|
||||
}
|
||||
|
||||
@ -145,13 +146,13 @@ func (p *ProjectAPI) Put() {
|
||||
}
|
||||
if !isProjectAdmin(p.userId, projectId) {
|
||||
beego.Warning("Current user, id:", p.userId, ", does not have project admin role for project, id:", projectId)
|
||||
p.RenderError(403, "")
|
||||
p.RenderError(http.StatusForbidden, "")
|
||||
return
|
||||
}
|
||||
err = dao.ToggleProjectPublicity(p.projectId, public)
|
||||
if err != nil {
|
||||
beego.Error("Error while updating project, project id:", projectId, ", error:", err)
|
||||
p.RenderError(500, "Failed to update project")
|
||||
p.RenderError(http.StatusInternalServerError, "Failed to update project")
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,14 +163,18 @@ func (p *ProjectAPI) FilterAccessLog() {
|
||||
|
||||
username := filter.Username
|
||||
keywords := filter.Keywords
|
||||
beginTime := filter.BeginTime
|
||||
endTime := filter.EndTime
|
||||
|
||||
query := models.AccessLog{ProjectId: p.projectId, Username: "%" + username + "%", Keywords: keywords, BeginTime: beginTime, EndTime: endTime}
|
||||
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}
|
||||
|
||||
log.Printf("Query AccessLog: begin: %v, end: %v, keywords: %s", query.BeginTime, query.EndTime, query.Keywords)
|
||||
|
||||
accessLogList, err := dao.GetAccessLogs(query)
|
||||
if err != nil {
|
||||
log.Printf("Error occurred in GetAccessLogs: %v", err)
|
||||
p.CustomAbort(500, "Internal error.")
|
||||
p.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
p.Data["json"] = accessLogList
|
||||
p.ServeJSON()
|
||||
|
@ -15,12 +15,13 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type ProjectMemberAPI struct {
|
||||
@ -40,18 +41,18 @@ func (pma *ProjectMemberAPI) Prepare() {
|
||||
pid, err := strconv.ParseInt(pma.Ctx.Input.Param(":pid"), 10, 64)
|
||||
if err != nil {
|
||||
beego.Error("Error parsing project id:", pid, ", error:", err)
|
||||
pma.CustomAbort(400, "invalid project Id")
|
||||
pma.CustomAbort(http.StatusBadRequest, "invalid project Id")
|
||||
return
|
||||
}
|
||||
p, err := dao.GetProjectById(models.Project{ProjectId: pid})
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetProjectById:", err)
|
||||
pma.CustomAbort(500, "Internal error.")
|
||||
pma.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
|
||||
if p == nil {
|
||||
beego.Warning("Project with id:", pid, "does not exist.")
|
||||
pma.CustomAbort(404, "Project does not exist")
|
||||
pma.CustomAbort(http.StatusNotFound, "Project does not exist")
|
||||
}
|
||||
pma.project = p
|
||||
pma.currentUserId = pma.ValidateUser()
|
||||
@ -64,7 +65,7 @@ func (pma *ProjectMemberAPI) Prepare() {
|
||||
memberId, err := strconv.Atoi(mid)
|
||||
if err != nil {
|
||||
beego.Error("Invalid member Id, error:", err)
|
||||
pma.CustomAbort(400, "Invalid member id")
|
||||
pma.CustomAbort(http.StatusBadRequest, "Invalid member id")
|
||||
}
|
||||
pma.memberId = memberId
|
||||
}
|
||||
@ -74,7 +75,7 @@ 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)
|
||||
pma.RenderError(403, "")
|
||||
pma.RenderError(http.StatusForbidden, "")
|
||||
return
|
||||
}
|
||||
if pma.memberId == 0 { //member id not set return list of the members
|
||||
@ -84,7 +85,7 @@ func (pma *ProjectMemberAPI) Get() {
|
||||
userList, err := dao.GetUserByProject(queryProject, queryUser)
|
||||
if err != nil {
|
||||
beego.Error("Failed to query database for member list, error:", err)
|
||||
pma.RenderError(500, "Internal Server Error")
|
||||
pma.RenderError(http.StatusInternalServerError, "Internal Server Error")
|
||||
return
|
||||
}
|
||||
pma.Data["json"] = userList
|
||||
@ -92,14 +93,14 @@ func (pma *ProjectMemberAPI) Get() {
|
||||
roleList, err := dao.GetUserProjectRoles(models.User{UserId: pma.memberId}, pid)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUserProjectRoles:", err)
|
||||
pma.CustomAbort(500, "Internal error.")
|
||||
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})
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUser:", err)
|
||||
pma.CustomAbort(500, "Internal error.")
|
||||
pma.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
result["user_name"] = user.Username
|
||||
result["user_id"] = pma.memberId
|
||||
@ -115,11 +116,11 @@ func (pma *ProjectMemberAPI) Post() {
|
||||
rolelist, err := dao.GetUserProjectRoles(userQuery, pid)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUserProjectRoles:", err)
|
||||
pma.CustomAbort(500, "Internal error.")
|
||||
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)
|
||||
pma.RenderError(403, "")
|
||||
pma.RenderError(http.StatusForbidden, "")
|
||||
return
|
||||
}
|
||||
var req memberReq
|
||||
@ -128,17 +129,17 @@ func (pma *ProjectMemberAPI) Post() {
|
||||
userId := CheckUserExists(username)
|
||||
if userId <= 0 {
|
||||
beego.Warning("User does not exist, user name:", username)
|
||||
pma.RenderError(404, "User does not exist")
|
||||
pma.RenderError(http.StatusNotFound, "User does not exist")
|
||||
return
|
||||
}
|
||||
rolelist, err = dao.GetUserProjectRoles(models.User{UserId: userId}, pid)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUserProjectRoles:", err)
|
||||
pma.CustomAbort(500, "Internal error.")
|
||||
pma.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if len(rolelist) > 0 {
|
||||
beego.Warning("user is already added to project, user id:", userId, ", project id:", pid)
|
||||
pma.RenderError(409, "user is ready in project")
|
||||
pma.RenderError(http.StatusConflict, "user is ready in project")
|
||||
return
|
||||
}
|
||||
|
||||
@ -146,7 +147,7 @@ func (pma *ProjectMemberAPI) Post() {
|
||||
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)
|
||||
pma.RenderError(500, "Failed to update data in database")
|
||||
pma.RenderError(http.StatusInternalServerError, "Failed to update data in database")
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -159,11 +160,11 @@ func (pma *ProjectMemberAPI) Put() {
|
||||
rolelist, err := dao.GetUserProjectRoles(userQuery, pid)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUserProjectRoles:", err)
|
||||
pma.CustomAbort(500, "Internal error.")
|
||||
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)
|
||||
pma.RenderError(403, "")
|
||||
pma.RenderError(http.StatusForbidden, "")
|
||||
return
|
||||
}
|
||||
var req memberReq
|
||||
@ -171,7 +172,7 @@ func (pma *ProjectMemberAPI) Put() {
|
||||
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(404, "user not exist in project")
|
||||
pma.RenderError(http.StatusNotFound, "user not exist in project")
|
||||
return
|
||||
}
|
||||
//TODO: delete and insert should in one transaction
|
||||
@ -179,7 +180,7 @@ func (pma *ProjectMemberAPI) Put() {
|
||||
err = dao.DeleteUserProjectRoles(mid, pid)
|
||||
if err != nil {
|
||||
beego.Error("Failed to delete project roles for user, user id:", mid, ", project id: ", pid, ", error: ", err)
|
||||
pma.RenderError(500, "Failed to update data in DB")
|
||||
pma.RenderError(http.StatusInternalServerError, "Failed to update data in DB")
|
||||
return
|
||||
}
|
||||
//insert roles in request
|
||||
@ -187,7 +188,7 @@ func (pma *ProjectMemberAPI) Put() {
|
||||
err = dao.AddUserProjectRole(mid, pid, int(rid))
|
||||
if err != nil {
|
||||
beego.Error("Failed to update DB to add project user role, project id:", pid, ", user id:", mid, ", role id:", rid)
|
||||
pma.RenderError(500, "Failed to update data in database")
|
||||
pma.RenderError(http.StatusInternalServerError, "Failed to update data in database")
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -200,13 +201,13 @@ func (pma *ProjectMemberAPI) Delete() {
|
||||
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)
|
||||
pma.RenderError(403, "")
|
||||
pma.RenderError(http.StatusForbidden, "")
|
||||
return
|
||||
}
|
||||
err = dao.DeleteUserProjectRoles(mid, pid)
|
||||
if err != nil {
|
||||
beego.Error("Failed to delete project roles for user, user id:", mid, ", project id:", pid, ", error:", err)
|
||||
pma.RenderError(500, "Failed to update data in DB")
|
||||
pma.RenderError(http.StatusInternalServerError, "Failed to update data in DB")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -56,28 +57,28 @@ func (ra *RepositoryAPI) Get() {
|
||||
projectId, err0 := ra.GetInt64("project_id")
|
||||
if err0 != nil {
|
||||
beego.Error("Failed to get project id, error:", err0)
|
||||
ra.RenderError(400, "Invalid project id")
|
||||
ra.RenderError(http.StatusBadRequest, "Invalid project id")
|
||||
return
|
||||
}
|
||||
projectQuery := models.Project{ProjectId: projectId}
|
||||
p, err := dao.GetProjectById(projectQuery)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetProjectById:", err)
|
||||
ra.CustomAbort(500, "Internal error.")
|
||||
ra.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if p == nil {
|
||||
beego.Warning("Project with Id:", projectId, ", does not exist", projectId)
|
||||
ra.RenderError(404, "")
|
||||
ra.RenderError(http.StatusNotFound, "")
|
||||
return
|
||||
}
|
||||
if p.Public == 0 && !CheckProjectPermission(ra.userId, projectId) {
|
||||
ra.RenderError(403, "")
|
||||
ra.RenderError(http.StatusForbidden, "")
|
||||
return
|
||||
}
|
||||
repoList, err := svc_utils.GetRepoFromCache()
|
||||
if err != nil {
|
||||
beego.Error("Failed to get repo from cache, error:", err)
|
||||
ra.RenderError(500, "internal sever error")
|
||||
ra.RenderError(http.StatusInternalServerError, "internal sever error")
|
||||
}
|
||||
projectName := p.Name
|
||||
q := ra.GetString("q")
|
||||
@ -127,7 +128,7 @@ func (ra *RepositoryAPI) GetTags() {
|
||||
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(500, "Failed to get repo tags")
|
||||
ra.RenderError(http.StatusInternalServerError, "Failed to get repo tags")
|
||||
} else {
|
||||
t := Tag{}
|
||||
json.Unmarshal(result, &t)
|
||||
@ -146,14 +147,14 @@ func (ra *RepositoryAPI) GetManifests() {
|
||||
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(500, "Internal Server Error")
|
||||
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(500, "Internal Server Error")
|
||||
ra.RenderError(http.StatusInternalServerError, "Internal Server Error")
|
||||
return
|
||||
} else {
|
||||
v1Compatibility := mani.History[0].V1Compatibility
|
||||
@ -161,7 +162,7 @@ func (ra *RepositoryAPI) GetManifests() {
|
||||
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(500, "Internal Server Error")
|
||||
ra.RenderError(http.StatusInternalServerError, "Internal Server Error")
|
||||
return
|
||||
} else {
|
||||
item.CreatedStr = item.Created.Format("2006-01-02 15:04:05")
|
||||
|
@ -15,6 +15,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@ -44,7 +45,7 @@ func (n *SearchAPI) Get() {
|
||||
projects, err := dao.QueryRelevantProjects(userId)
|
||||
if err != nil {
|
||||
beego.Error("Failed to get projects of user id:", userId, ", error:", err)
|
||||
n.CustomAbort(500, "Failed to get project search result")
|
||||
n.CustomAbort(http.StatusInternalServerError, "Failed to get project search result")
|
||||
}
|
||||
projectSorter := &utils.ProjectSorter{Projects: projects}
|
||||
sort.Sort(projectSorter)
|
||||
@ -66,7 +67,7 @@ func (n *SearchAPI) Get() {
|
||||
repositories, err2 := svc_utils.GetRepoFromCache()
|
||||
if err2 != nil {
|
||||
beego.Error("Failed to get repos from cache, error :", err2)
|
||||
n.CustomAbort(500, "Failed to get repositories search result")
|
||||
n.CustomAbort(http.StatusInternalServerError, "Failed to get repositories search result")
|
||||
}
|
||||
sort.Strings(repositories)
|
||||
repositoryResult := filterRepositories(repositories, projects, keyword)
|
||||
|
27
api/user.go
27
api/user.go
@ -15,6 +15,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/vmware/harbor/dao"
|
||||
@ -40,17 +41,17 @@ func (ua *UserAPI) Prepare() {
|
||||
ua.userId, err = strconv.Atoi(id)
|
||||
if err != nil {
|
||||
beego.Error("Invalid user id, error:", err)
|
||||
ua.CustomAbort(400, "Invalid user Id")
|
||||
ua.CustomAbort(http.StatusBadRequest, "Invalid user Id")
|
||||
}
|
||||
userQuery := models.User{UserId: ua.userId}
|
||||
u, err := dao.GetUser(userQuery)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUser:", err)
|
||||
ua.CustomAbort(500, "Internal error.")
|
||||
ua.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if u == nil {
|
||||
beego.Error("User with Id:", ua.userId, "does not exist")
|
||||
ua.CustomAbort(404, "")
|
||||
ua.CustomAbort(http.StatusNotFound, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -59,13 +60,13 @@ func (ua *UserAPI) Get() {
|
||||
exist, err := dao.IsAdminRole(ua.currentUid)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in IsAdminRole:", err)
|
||||
ua.CustomAbort(500, "Internal error.")
|
||||
ua.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
|
||||
if ua.userId == 0 { //list users
|
||||
if !exist {
|
||||
beego.Error("Current user, id:", ua.currentUid, ", does not have admin role, can not list users")
|
||||
ua.RenderError(403, "User does not have admin role")
|
||||
ua.RenderError(http.StatusForbidden, "User does not have admin role")
|
||||
return
|
||||
}
|
||||
username := ua.GetString("username")
|
||||
@ -76,7 +77,7 @@ func (ua *UserAPI) Get() {
|
||||
userList, err := dao.ListUsers(userQuery)
|
||||
if err != nil {
|
||||
beego.Error("Failed to get data from database, error:", err)
|
||||
ua.RenderError(500, "Failed to query from database")
|
||||
ua.RenderError(http.StatusInternalServerError, "Failed to query from database")
|
||||
return
|
||||
}
|
||||
ua.Data["json"] = userList
|
||||
@ -86,12 +87,12 @@ func (ua *UserAPI) Get() {
|
||||
u, err := dao.GetUser(userQuery)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUser:", err)
|
||||
ua.CustomAbort(500, "Internal error.")
|
||||
ua.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
ua.Data["json"] = u
|
||||
} else {
|
||||
beego.Error("Current user, id:", ua.currentUid, "does not have admin role, can not view other user's detail")
|
||||
ua.RenderError(403, "User does not have admin role")
|
||||
ua.RenderError(http.StatusForbidden, "User does not have admin role")
|
||||
return
|
||||
}
|
||||
ua.ServeJSON()
|
||||
@ -101,11 +102,11 @@ func (ua *UserAPI) Put() { //currently only for toggle admin, so no request body
|
||||
exist, err := dao.IsAdminRole(ua.currentUid)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in IsAdminRole:", err)
|
||||
ua.CustomAbort(500, "Internal error.")
|
||||
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")
|
||||
ua.RenderError(403, "User does not have admin role")
|
||||
ua.RenderError(http.StatusForbidden, "User does not have admin role")
|
||||
return
|
||||
}
|
||||
userQuery := models.User{UserId: ua.userId}
|
||||
@ -116,17 +117,17 @@ func (ua *UserAPI) Delete() {
|
||||
exist, err := dao.IsAdminRole(ua.currentUid)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in IsAdminRole:", err)
|
||||
ua.CustomAbort(500, "Internal error.")
|
||||
ua.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if !exist {
|
||||
beego.Warning("current user, id:", ua.currentUid, ", does not have admin role, can not remove user")
|
||||
ua.RenderError(403, "User does not have admin role")
|
||||
ua.RenderError(http.StatusForbidden, "User does not have admin role")
|
||||
return
|
||||
}
|
||||
err = dao.DeleteUser(ua.userId)
|
||||
if err != nil {
|
||||
beego.Error("Failed to delete data from database, error:", err)
|
||||
ua.RenderError(500, "Failed to delete User")
|
||||
ua.RenderError(http.StatusInternalServerError, "Failed to delete User")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
appname = harbor
|
||||
runmode = dev
|
||||
|
||||
[lang]
|
||||
types = en-US|zh-CN
|
||||
names = en-US|zh-CN
|
||||
|
||||
[dev]
|
||||
httpport = 80
|
@ -1,15 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXQIBAAKBgQClak/4HO7EeLU0w/BhtVENPLOqU0AP2QjVUdg1qhNiDWVrbWx9
|
||||
KYHqz5Kn0n2+fxdZo3o7ZY5/2+hhgkKh1z6Kge9XGgune6z4fx2J/X2Se8WsGeQU
|
||||
TiND8ngSnsCANtYFwW50SbUZPtyf5XjAfKRofZem51OxbxzN3217L/ubKwIDAQAB
|
||||
AoGBAITMMuNYJwAogCGaZHOs4yMjZoIJT9bpQMQxbsi2f9UqOA/ky0I4foqKloyQ
|
||||
2k6DLbXTHqBsydgwLgGKWAAiE5xIR2bPMUNSLgjbA2eLly3aOR/0FJ5n09k2EmGg
|
||||
Am7tLP+6yneXWKVi3HI3NzXriVjWK94WHGGC1b9F+n5CY/2RAkEA1d62OJUNve2k
|
||||
IY6/b6T0BdssFo3VFcm22vnayEL/wcYrnRfF9Pb5wM4HUUqwVelKTouivXg60GNK
|
||||
ZKYAx5CtHwJBAMYAEf5u0CQ/8URcwBuMkm0LzK4AM2x1nGs7gIxAEFhu1Z4xPjVe
|
||||
MtIxuHhDhlLvD760uccmo5yE72QJ1ZrYBHUCQQCAxLZMPRpoB4QyHEOREe1G9V6H
|
||||
OeBZXPk2wQcEWqqo3gt2a1DqHCXl+2aWgHTJVUxDHHngwFoRDCdHkFeZ0LcbAkAj
|
||||
T8/luI2WaXD16DS6tQ9IM1qFjbOeHDuRRENgv+wqWVnvpIibq/kUU5m6mRBTqh78
|
||||
u+6F/fYf6/VluftGalAhAkAukdMtt+sksq2e7Qw2dRr5GXtXjt+Otjj0NaJENmWk
|
||||
a7SgAs34EOWtbd0XGYpZFrg134MzQGbweFeEUTj++e8p
|
||||
-----END RSA PRIVATE KEY-----
|
@ -15,6 +15,7 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
@ -114,6 +115,16 @@ var langTypes []*langType
|
||||
|
||||
func init() {
|
||||
|
||||
//conf/app.conf -> os.Getenv("config_path")
|
||||
configPath := os.Getenv("CONFIG_PATH")
|
||||
if len(configPath) != 0 {
|
||||
log.Printf("Config path: %s", configPath)
|
||||
beego.AppConfigPath = configPath
|
||||
if err := beego.ParseConfig(); err != nil {
|
||||
beego.Warning("Failed to parse config file: ", configPath, "error: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
beego.AddFuncMap("i18n", i18n.Tr)
|
||||
|
||||
langs := strings.Split(beego.AppConfig.String("lang::types"), "|")
|
||||
@ -134,7 +145,6 @@ func init() {
|
||||
for _, lang := range langs {
|
||||
if err := i18n.SetMessage(lang, "static/i18n/"+"locale_"+lang+".ini"); err != nil {
|
||||
beego.Error("Fail to set message file:" + err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
@ -68,7 +69,7 @@ func (idc *ItemDetailController) Get() {
|
||||
projectId, _ := idc.GetInt64("project_id")
|
||||
|
||||
if CheckPublicProject(projectId) == false && (sessionUserId == nil || !CheckProjectRole(sessionUserId.(int), projectId)) {
|
||||
idc.Redirect("/signIn?uri="+url.QueryEscape(idc.Ctx.Input.URI()), 302)
|
||||
idc.Redirect("/signIn?uri="+url.QueryEscape(idc.Ctx.Input.URI()), http.StatusFound)
|
||||
}
|
||||
|
||||
projectQuery := models.Project{ProjectId: projectId}
|
||||
@ -76,11 +77,11 @@ func (idc *ItemDetailController) Get() {
|
||||
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetProjectById:", err)
|
||||
idc.CustomAbort(500, "Internal error.")
|
||||
idc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
|
||||
if project == nil {
|
||||
idc.Redirect("/signIn", 302)
|
||||
idc.Redirect("/signIn", http.StatusFound)
|
||||
}
|
||||
|
||||
idc.Data["ProjectId"] = project.ProjectId
|
||||
@ -94,7 +95,7 @@ func (idc *ItemDetailController) Get() {
|
||||
roleList, err := dao.GetUserProjectRoles(models.User{UserId: sessionUserId.(int)}, projectId)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUserProjectRoles:", err)
|
||||
idc.CustomAbort(500, "Internal error.")
|
||||
idc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if len(roleList) > 0 {
|
||||
idc.Data["RoleId"] = roleList[0].RoleId
|
||||
|
@ -15,6 +15,8 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/harbor/models"
|
||||
"github.com/vmware/harbor/opt_auth"
|
||||
|
||||
@ -45,11 +47,11 @@ func (c *CommonController) Login() {
|
||||
user, err := opt_auth.Login(models.AuthModel{principal, password})
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in UserLogin:", err)
|
||||
c.CustomAbort(500, "Internal error.")
|
||||
c.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
c.CustomAbort(401, "")
|
||||
c.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
|
||||
c.SetSession("userId", user.UserId)
|
||||
@ -62,7 +64,7 @@ func (c *CommonController) SwitchLanguage() {
|
||||
c.SetSession("lang", lang)
|
||||
c.Data["Lang"] = lang
|
||||
}
|
||||
c.Redirect(c.Ctx.Request.Header.Get("Referer"), 302)
|
||||
c.Redirect(c.Ctx.Request.Header.Get("Referer"), http.StatusFound)
|
||||
}
|
||||
|
||||
func (c *CommonController) Logout() {
|
||||
|
@ -16,6 +16,7 @@ package controllers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"text/template"
|
||||
@ -34,7 +35,7 @@ type ChangePasswordController struct {
|
||||
func (cpc *ChangePasswordController) Get() {
|
||||
sessionUserId := cpc.GetSession("userId")
|
||||
if sessionUserId == nil {
|
||||
cpc.Redirect("/signIn", 302)
|
||||
cpc.Redirect("/signIn", http.StatusFound)
|
||||
}
|
||||
cpc.Data["Username"] = cpc.GetSession("username")
|
||||
cpc.ForwardTo("page_title_change_password", "change-password")
|
||||
@ -43,32 +44,40 @@ func (cpc *ChangePasswordController) Get() {
|
||||
func (cpc *CommonController) UpdatePassword() {
|
||||
|
||||
sessionUserId := cpc.GetSession("userId")
|
||||
sessionUsername := cpc.GetSession("username")
|
||||
|
||||
if sessionUserId == nil || sessionUsername == nil {
|
||||
if sessionUserId == nil {
|
||||
beego.Warning("User does not login.")
|
||||
cpc.CustomAbort(401, "please_login_first")
|
||||
cpc.CustomAbort(http.StatusUnauthorized, "please_login_first")
|
||||
}
|
||||
|
||||
oldPassword := cpc.GetString("old_password")
|
||||
queryUser := models.User{UserId: sessionUserId.(int), Username: sessionUsername.(string), Password: oldPassword}
|
||||
if oldPassword == "" {
|
||||
beego.Error("Old password is blank")
|
||||
cpc.CustomAbort(http.StatusBadRequest, "Old password is blank")
|
||||
}
|
||||
|
||||
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(500, "Internal error.")
|
||||
cpc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
beego.Warning("Password input is not correct")
|
||||
cpc.CustomAbort(403, "old_password_is_not_correct")
|
||||
cpc.CustomAbort(http.StatusForbidden, "old_password_is_not_correct")
|
||||
}
|
||||
|
||||
password := cpc.GetString("password")
|
||||
if password != "" {
|
||||
updateUser := models.User{UserId: sessionUserId.(int), Username: sessionUsername.(string), Password: password, Salt: user.Salt}
|
||||
dao.ChangeUserPassword(updateUser)
|
||||
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.")
|
||||
}
|
||||
} else {
|
||||
cpc.CustomAbort(404, "please_input_new_password")
|
||||
cpc.CustomAbort(http.StatusBadRequest, "please_input_new_password")
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,22 +99,26 @@ func (fpc *CommonController) SendEmail() {
|
||||
|
||||
email := fpc.GetString("email")
|
||||
|
||||
if ok, _ := 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); ok {
|
||||
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")
|
||||
} else {
|
||||
|
||||
queryUser := models.User{Email: email}
|
||||
exist, err := dao.UserExists(queryUser, "email")
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in UserExists:", err)
|
||||
fpc.CustomAbort(500, "Internal error.")
|
||||
fpc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if !exist {
|
||||
fpc.CustomAbort(404, "email_does_not_exist")
|
||||
fpc.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(500, err.Error())
|
||||
fpc.CustomAbort(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
message := new(bytes.Buffer)
|
||||
@ -117,7 +130,7 @@ func (fpc *CommonController) SendEmail() {
|
||||
uuid, err := dao.GenerateRandomString()
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GenerateRandomString:", err)
|
||||
fpc.CustomAbort(500, "Internal error.")
|
||||
fpc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
err = messageTemplate.Execute(message, MessageDetail{
|
||||
Hint: fpc.Tr("reset_email_hint"),
|
||||
@ -127,13 +140,13 @@ func (fpc *CommonController) SendEmail() {
|
||||
|
||||
if err != nil {
|
||||
beego.Error("message template error:", err)
|
||||
fpc.CustomAbort(500, "internal_error")
|
||||
fpc.CustomAbort(http.StatusInternalServerError, "internal_error")
|
||||
}
|
||||
|
||||
config, err := beego.AppConfig.GetSection("mail")
|
||||
if err != nil {
|
||||
beego.Error("Can not load app.conf:", err)
|
||||
fpc.CustomAbort(500, "internal_error")
|
||||
fpc.CustomAbort(http.StatusInternalServerError, "internal_error")
|
||||
}
|
||||
|
||||
mail := utils.Mail{
|
||||
@ -146,14 +159,12 @@ func (fpc *CommonController) SendEmail() {
|
||||
|
||||
if err != nil {
|
||||
beego.Error("send email failed:", err)
|
||||
fpc.CustomAbort(500, "send_email_failed")
|
||||
fpc.CustomAbort(http.StatusInternalServerError, "send_email_failed")
|
||||
}
|
||||
|
||||
user := models.User{ResetUuid: uuid, Email: email}
|
||||
dao.UpdateUserResetUuid(user)
|
||||
|
||||
} else {
|
||||
fpc.CustomAbort(409, "email_content_illegal")
|
||||
}
|
||||
|
||||
}
|
||||
@ -164,39 +175,55 @@ type ResetPasswordController struct {
|
||||
|
||||
func (rpc *ResetPasswordController) Get() {
|
||||
|
||||
q := rpc.GetString("q")
|
||||
queryUser := models.User{ResetUuid: q}
|
||||
resetUuid := rpc.GetString("reset_uuid")
|
||||
if resetUuid == "" {
|
||||
beego.Error("Reset uuid is blank.")
|
||||
rpc.Redirect("/", http.StatusFound)
|
||||
}
|
||||
|
||||
queryUser := models.User{ResetUuid: resetUuid}
|
||||
user, err := dao.GetUser(queryUser)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUser:", err)
|
||||
rpc.CustomAbort(500, "Internal error.")
|
||||
rpc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
|
||||
if user != nil {
|
||||
rpc.Data["ResetUuid"] = user.ResetUuid
|
||||
rpc.ForwardTo("page_title_reset_password", "reset-password")
|
||||
} else {
|
||||
rpc.Redirect("/", 302)
|
||||
rpc.Redirect("/", http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
func (rpc *CommonController) ResetPassword() {
|
||||
|
||||
resetUuid := rpc.GetString("reset_uuid")
|
||||
if resetUuid == "" {
|
||||
rpc.CustomAbort(http.StatusBadRequest, "Reset uuid is blank.")
|
||||
}
|
||||
|
||||
queryUser := models.User{ResetUuid: resetUuid}
|
||||
user, err := dao.GetUser(queryUser)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUser:", err)
|
||||
rpc.CustomAbort(500, "Internal error.")
|
||||
rpc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if user == nil {
|
||||
beego.Error("User does not exist")
|
||||
rpc.CustomAbort(http.StatusBadRequest, "User does not exist")
|
||||
}
|
||||
|
||||
password := rpc.GetString("password")
|
||||
|
||||
if password != "" {
|
||||
user.Password = password
|
||||
dao.ResetUserPassword(*user)
|
||||
err = dao.ResetUserPassword(*user)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in ResetUserPassword:", err)
|
||||
rpc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
} else {
|
||||
rpc.CustomAbort(404, "password_is_required")
|
||||
rpc.CustomAbort(http.StatusBadRequest, "password_is_required")
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
@ -33,7 +34,7 @@ func (rc *RegisterController) Get() {
|
||||
if authMode == "" || authMode == "db_auth" {
|
||||
rc.ForwardTo("page_title_registration", "register")
|
||||
} else {
|
||||
rc.Redirect("/signIn", 404)
|
||||
rc.Redirect("/signIn", http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,7 +50,7 @@ func (rc *CommonController) SignUp() {
|
||||
_, err := dao.Register(user)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in Register:", err)
|
||||
rc.CustomAbort(500, "Internal error.")
|
||||
rc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +69,7 @@ func (rc *CommonController) UserExists() {
|
||||
exist, err := dao.UserExists(user, target)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in UserExists:", err)
|
||||
rc.CustomAbort(500, "Internal error.")
|
||||
rc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
rc.Data["json"] = exist
|
||||
rc.ServeJSON()
|
||||
|
@ -73,13 +73,13 @@ func GetAccessLogs(accessLog models.AccessLog) ([]models.AccessLog, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if accessLog.BeginTime != "" {
|
||||
sql += ` and a.op_time >= str_to_date(?, '%Y-%m-%d %H:%i:%s') `
|
||||
queryParam = append(queryParam, accessLog.BeginTime+" 00:00:00.000000")
|
||||
if accessLog.BeginTimestamp > 0 {
|
||||
sql += ` and a.op_time >= ? `
|
||||
queryParam = append(queryParam, accessLog.BeginTime)
|
||||
}
|
||||
if accessLog.EndTime != "" {
|
||||
sql += ` and a.op_time <= str_to_date(?, '%Y-%m-%d %H:%i:%s') `
|
||||
queryParam = append(queryParam, accessLog.EndTime+" 23:59:59.99999")
|
||||
if accessLog.EndTimestamp > 0 {
|
||||
sql += ` and a.op_time <= ? `
|
||||
queryParam = append(queryParam, accessLog.EndTime)
|
||||
}
|
||||
|
||||
sql += ` order by a.op_time desc `
|
||||
|
42
dao/base.go
42
dao/base.go
@ -1,16 +1,16 @@
|
||||
/*
|
||||
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 dao
|
||||
|
||||
@ -50,18 +50,17 @@ func isContainIllegalChar(s string, illegalChar []string) bool {
|
||||
|
||||
func GenerateRandomString() (string, error) {
|
||||
o := orm.NewOrm()
|
||||
var salt string
|
||||
err := o.Raw(`select uuid() as uuid`).QueryRow(&salt)
|
||||
var uuid string
|
||||
err := o.Raw(`select uuid() as uuid`).QueryRow(&uuid)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return salt, nil
|
||||
return uuid, nil
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
func InitDB() {
|
||||
orm.RegisterDriver("mysql", orm.DRMySQL)
|
||||
|
||||
addr := os.Getenv("MYSQL_HOST")
|
||||
if len(addr) == 0 {
|
||||
addr = os.Getenv("MYSQL_PORT_3306_TCP_ADDR")
|
||||
@ -112,8 +111,11 @@ func init() {
|
||||
}()
|
||||
select {
|
||||
case <-ch:
|
||||
case <-time.After(30 * time.Second):
|
||||
panic("Failed to connect to DB after 30 seconds")
|
||||
case <-time.After(60 * time.Second):
|
||||
panic("Failed to connect to DB after 60 seconds")
|
||||
}
|
||||
err := orm.RegisterDataBase("default", "mysql", db_str)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
orm.RegisterDataBase("default", "mysql", db_str)
|
||||
}
|
||||
|
38
dao/user.go
38
dao/user.go
@ -15,6 +15,9 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"github.com/vmware/harbor/models"
|
||||
"github.com/vmware/harbor/utils"
|
||||
|
||||
@ -133,15 +136,44 @@ func ToggleUserAdminRole(u models.User) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func ChangeUserPassword(u models.User) error {
|
||||
func ChangeUserPassword(u models.User, oldPassword ...string) error {
|
||||
o := orm.NewOrm()
|
||||
_, err := o.Raw(`update user set password=?, salt=? where user_id=?`, utils.Encrypt(u.Password, u.Salt), u.Salt, u.UserId).Exec()
|
||||
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()
|
||||
} 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()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count, err := r.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
return errors.New("No record be changed, change password failed.")
|
||||
}
|
||||
} else {
|
||||
return errors.New("Wrong numbers of params.")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func ResetUserPassword(u models.User) error {
|
||||
o := orm.NewOrm()
|
||||
_, 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
|
||||
}
|
||||
count, err := r.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
return errors.New("No record be changed, reset password failed.")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
12
main.go
12
main.go
@ -38,7 +38,7 @@ 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 in initial password, userId:", userId)
|
||||
log.Println("Failed to get user, userId:", userId)
|
||||
return err
|
||||
}
|
||||
if user == nil {
|
||||
@ -67,15 +67,7 @@ func updateInitPassword(userId int, password string) error {
|
||||
func main() {
|
||||
|
||||
beego.BConfig.WebConfig.Session.SessionOn = true
|
||||
|
||||
//conf/app.conf -> os.Getenv("config_path")
|
||||
configPath := os.Getenv("CONFIG_PATH")
|
||||
if len(configPath) != 0 {
|
||||
beego.Debug(fmt.Sprintf("Config path: %s", configPath))
|
||||
beego.AppConfigPath = configPath
|
||||
}
|
||||
|
||||
dao.InitDB()
|
||||
updateInitPassword(ADMIN_USER_ID, os.Getenv("HARBOR_ADMIN_PASSWORD"))
|
||||
|
||||
beego.Run()
|
||||
}
|
||||
|
@ -26,10 +26,11 @@ type AccessLog struct {
|
||||
Guid string
|
||||
Operation string
|
||||
OpTime time.Time
|
||||
OpTimeStr string
|
||||
|
||||
Username string
|
||||
Keywords string
|
||||
BeginTime string
|
||||
EndTime string
|
||||
|
||||
BeginTime time.Time
|
||||
BeginTimestamp int64
|
||||
EndTime time.Time
|
||||
EndTimestamp int64
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ package service
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/harbor/models"
|
||||
"github.com/vmware/harbor/opt_auth"
|
||||
@ -46,7 +47,7 @@ func (a *AuthController) Auth() {
|
||||
|
||||
if len(scope) == 0 && !authenticated {
|
||||
log.Printf("login request with invalid credentials")
|
||||
a.CustomAbort(401, "")
|
||||
a.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
access := svc_utils.GetResourceActions(scope)
|
||||
for _, a := range access {
|
||||
@ -61,7 +62,7 @@ func (a *AuthController) serveToken(username, service string, access []*token.Re
|
||||
rawToken, err := svc_utils.MakeToken(username, service, access)
|
||||
if err != nil {
|
||||
log.Printf("Failed to make token, error: %v", err)
|
||||
writer.WriteHeader(500)
|
||||
writer.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
tk := make(map[string]string)
|
||||
|
@ -50,9 +50,9 @@ func RegistryApiGet(url, username string) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode == 200 {
|
||||
if response.StatusCode == http.StatusOK {
|
||||
return result, nil
|
||||
} else if response.StatusCode == 401 {
|
||||
} else if response.StatusCode == http.StatusUnauthorized {
|
||||
authenticate := response.Header.Get("WWW-Authenticate")
|
||||
str := strings.Split(authenticate, " ")[1]
|
||||
log.Println("url: " + url)
|
||||
@ -94,7 +94,7 @@ func RegistryApiGet(url, username string) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if response.StatusCode != 200 {
|
||||
if response.StatusCode != http.StatusOK {
|
||||
errMsg := fmt.Sprintf("Unexpected return code from registry: %d", response.StatusCode)
|
||||
log.Printf(errMsg)
|
||||
return nil, fmt.Errorf(errMsg)
|
||||
|
@ -374,24 +374,38 @@ jQuery(function(){
|
||||
listUser(username);
|
||||
});
|
||||
|
||||
function toUTCSeconds(date, hour, min, sec) {
|
||||
var t = new Date(date);
|
||||
t.setHours(hour);
|
||||
t.setMinutes(min);
|
||||
t.setSeconds(sec);
|
||||
var utcTime = new Date(t.getUTCFullYear(),
|
||||
t.getUTCMonth(),
|
||||
t.getUTCDate(),
|
||||
t.getUTCHours(),
|
||||
t.getUTCMinutes(),
|
||||
t.getUTCSeconds());
|
||||
return utcTime.getTime() / 1000;
|
||||
}
|
||||
|
||||
$("#btnFilterLog").on("click", function(){
|
||||
|
||||
var projectId = $("#projectId").val();
|
||||
var username = $("#txtSearchUserName").val();
|
||||
|
||||
var beginTime = "";
|
||||
var endTime = "";
|
||||
var beginTimestamp = 0;
|
||||
var endTimestamp = 0;
|
||||
|
||||
if($("#begindatepicker").val() != ""){
|
||||
beginTime = moment(new Date($("#begindatepicker").val())).format("YYYY-MM-DD");
|
||||
beginTimestamp = toUTCSeconds($("#begindatepicker").val(), 0, 0, 0);
|
||||
}
|
||||
if($("#enddatepicker").val() != ""){
|
||||
endTime = moment(new Date($("#enddatepicker").val())).format("YYYY-MM-DD");
|
||||
endTimestamp = toUTCSeconds($("#enddatepicker").val(), 23, 59, 59);
|
||||
}
|
||||
|
||||
new AjaxUtil({
|
||||
url: "/api/projects/" + projectId + "/logs/filter",
|
||||
data:{"username":username, "project_id" : projectId, "keywords" : getKeyWords() , "beginTime" : beginTime, "endTime" : endTime},
|
||||
data:{"username":username, "project_id" : projectId, "keywords" : getKeyWords() , "beginTimestamp" : beginTimestamp, "endTimestamp" : endTimestamp},
|
||||
type: "post",
|
||||
success: function(data, status, xhr){
|
||||
if(xhr && xhr.status == 200){
|
||||
|
@ -15,8 +15,8 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
// "fmt"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
@ -85,9 +85,7 @@ func clearUp(username string) {
|
||||
}
|
||||
|
||||
const USERNAME string = "Tester01"
|
||||
|
||||
const PROJECT_NAME string = "test_project"
|
||||
|
||||
const SYS_ADMIN int = 1
|
||||
const PROJECT_ADMIN int = 2
|
||||
const DEVELOPER int = 3
|
||||
@ -98,35 +96,37 @@ const PUBLICITY_OFF = 0
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
||||
//Create a custom flag set, let user to provide DB related configures for testing.
|
||||
fs := flag.NewFlagSet("DB related configures", 0)
|
||||
|
||||
dbIp := fs.String("db_ip", "localhost", "IP address for connecting a test DB.")
|
||||
dbPort := fs.String("db_port", "3306", "Port number for connecting a test DB.")
|
||||
dbUser := fs.String("db_user", "root", "Username for logging in a test DB.")
|
||||
dbPassword := fs.String("db_password", "root", "Password for logging in a test DB.")
|
||||
|
||||
fs.Parse([]string{"db_ip", "db_port", "db_user", "db_password"})
|
||||
|
||||
if fs.NFlag() == 0 {
|
||||
fs.PrintDefaults()
|
||||
fmt.Println("Now, use DEFAULT values if omit to set all of flags.")
|
||||
dbHost := os.Getenv("DB_HOST")
|
||||
if len(dbHost) == 0 {
|
||||
log.Fatalf("environment variable DB_HOST is not set")
|
||||
}
|
||||
dbUser := os.Getenv("DB_USR")
|
||||
if len(dbUser) == 0 {
|
||||
log.Fatalf("environment variable DB_USR is not set")
|
||||
}
|
||||
dbPort := os.Getenv("DB_PORT")
|
||||
if len(dbPort) == 0 {
|
||||
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")
|
||||
}
|
||||
|
||||
if fs.Parsed() {
|
||||
fmt.Printf("DB_HOST: %s, DB_USR: %s, DB_PORT: %s, DB_PWD: %s\n", dbHost, dbUser, dbPort, dbPassword)
|
||||
|
||||
clearUp(USERNAME)
|
||||
os.Setenv("MYSQL_PORT_3306_TCP_ADDR", dbHost)
|
||||
os.Setenv("MYSQL_PORT_3306_TCP_PORT", dbPort)
|
||||
os.Setenv("MYSQL_USR", dbUser)
|
||||
os.Setenv("MYSQL_PWD", dbPassword)
|
||||
os.Setenv("AUTH_MODE", "db_auth")
|
||||
dao.InitDB()
|
||||
clearUp(USERNAME)
|
||||
os.Exit(m.Run())
|
||||
|
||||
os.Setenv("MYSQL_PORT_3306_TCP_ADDR", *dbIp)
|
||||
os.Setenv("MYSQL_PORT_3306_TCP_PORT", *dbPort)
|
||||
os.Setenv("MYSQL_USR", *dbUser)
|
||||
os.Setenv("MYSQL_PWD", *dbPassword)
|
||||
os.Setenv("AUTH_MODE", "db_auth")
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleRegister() {
|
||||
func TestRegister(t *testing.T) {
|
||||
|
||||
user := models.User{
|
||||
Username: USERNAME,
|
||||
@ -138,7 +138,7 @@ func ExampleRegister() {
|
||||
|
||||
_, err := dao.Register(user)
|
||||
if err != nil {
|
||||
log.Printf("Error occurred in Register: %v", err)
|
||||
t.Errorf("Error occurred in Register: %v", err)
|
||||
}
|
||||
|
||||
//Check if user registered successfully.
|
||||
@ -147,41 +147,46 @@ func ExampleRegister() {
|
||||
}
|
||||
newUser, err := dao.GetUser(queryUser)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in GetUser: %v", err)
|
||||
t.Errorf("Error occurred in GetUser: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println(newUser.Username)
|
||||
fmt.Println(newUser.Email)
|
||||
// Output:
|
||||
// Tester01
|
||||
// tester01@vmware.com
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleUserExists() {
|
||||
func TestUserExists(t *testing.T) {
|
||||
var exists bool
|
||||
var err error
|
||||
|
||||
exists, err = dao.UserExists(models.User{Username: "Tester01"}, "username")
|
||||
fmt.Println(exists)
|
||||
|
||||
exists, err = dao.UserExists(models.User{Username: USERNAME}, "username")
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in UserExists: %v", err)
|
||||
t.Errorf("Error occurred in UserExists: %v", err)
|
||||
}
|
||||
if !exists {
|
||||
t.Errorf("User %s was inserted but does not exist", USERNAME)
|
||||
}
|
||||
exists, err = dao.UserExists(models.User{Email: "tester01@vmware.com"}, "email")
|
||||
fmt.Println(exists)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in UserExists: %v", err)
|
||||
t.Errorf("Error occurred in UserExists: %v", err)
|
||||
}
|
||||
if !exists {
|
||||
t.Errorf("User with email %s inserted but does not exist", "tester01@vmware.com")
|
||||
}
|
||||
exists, err = dao.UserExists(models.User{Username: "NOTHERE"}, "username")
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in UserExists: %v", err)
|
||||
}
|
||||
if exists {
|
||||
t.Errorf("User %s was not inserted but does exist", "NOTHERE")
|
||||
}
|
||||
|
||||
//Output:
|
||||
//true
|
||||
//true
|
||||
|
||||
}
|
||||
|
||||
func ExampleLoginByUserName() {
|
||||
func TestLoginByUserName(t *testing.T) {
|
||||
|
||||
userQuery := models.User{
|
||||
Username: USERNAME,
|
||||
@ -190,18 +195,18 @@ func ExampleLoginByUserName() {
|
||||
|
||||
loginUser, err := dao.LoginByDb(models.AuthModel{userQuery.Username, userQuery.Password})
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in LoginByDb: %v", err)
|
||||
t.Errorf("Error occurred in LoginByDb: %v", err)
|
||||
}
|
||||
if loginUser == nil {
|
||||
log.Fatalf("No found for user logined by username and password: %v", userQuery)
|
||||
t.Errorf("No found for user logined by username and password: %v", userQuery)
|
||||
}
|
||||
|
||||
fmt.Println(loginUser.Username)
|
||||
// Output:
|
||||
// Tester01
|
||||
if loginUser.Username != USERNAME {
|
||||
t.Errorf("User's username does not match after login, expected: %s, actual: %s", USERNAME, loginUser.Username)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleLoginByEmail() {
|
||||
func TestLoginByEmail(t *testing.T) {
|
||||
|
||||
userQuery := models.User{
|
||||
Email: "tester01@vmware.com",
|
||||
@ -210,105 +215,136 @@ func ExampleLoginByEmail() {
|
||||
|
||||
loginUser, err := dao.LoginByDb(models.AuthModel{userQuery.Email, userQuery.Password})
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in LoginByDb: %v", err)
|
||||
t.Errorf("Error occurred in LoginByDb: %v", err)
|
||||
}
|
||||
if loginUser == nil {
|
||||
log.Fatalf("No found for user logined by email and password : %v", userQuery)
|
||||
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)
|
||||
}
|
||||
fmt.Println(loginUser.Username)
|
||||
// Output:
|
||||
// Tester01
|
||||
}
|
||||
|
||||
var currentUser *models.User
|
||||
|
||||
func ExampleGetUser() {
|
||||
func TestGetUser(t *testing.T) {
|
||||
queryUser := models.User{
|
||||
Username: USERNAME,
|
||||
}
|
||||
var err error
|
||||
currentUser, err = dao.GetUser(queryUser)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in GetUser", err)
|
||||
t.Errorf("Error occurred in GetUser: %v", err)
|
||||
}
|
||||
if currentUser == nil {
|
||||
log.Fatalf("No user found queried by username: %v", queryUser)
|
||||
t.Errorf("No user found queried by user query: %+v", queryUser)
|
||||
}
|
||||
if currentUser.Email != "tester01@vmware.com" {
|
||||
t.Errorf("the user's email does not match, expected: tester01@vmware.com, actual: %s", currentUser.Email)
|
||||
}
|
||||
fmt.Println(currentUser.Username)
|
||||
//Output:
|
||||
//Tester01
|
||||
}
|
||||
|
||||
func ExampleListUsers() {
|
||||
users, err := dao.ListUsers(models.User{Username: "tester01"})
|
||||
func TestListUsers(t *testing.T) {
|
||||
users, err := dao.ListUsers(models.User{})
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in ListUsers: %v", err)
|
||||
t.Errorf("Error occurred in ListUsers: %v", err)
|
||||
}
|
||||
|
||||
for _, u := range users {
|
||||
fmt.Println(u.Username)
|
||||
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 := dao.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)
|
||||
}
|
||||
//Output:
|
||||
//Tester01
|
||||
}
|
||||
|
||||
func ExampleResetUserPassword() {
|
||||
func TestResetUserPassword(t *testing.T) {
|
||||
uuid, err := dao.GenerateRandomString()
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in GenerateRandomString: %v", err)
|
||||
t.Errorf("Error occurred in GenerateRandomString: %v", err)
|
||||
}
|
||||
|
||||
err = dao.UpdateUserResetUuid(models.User{ResetUuid: uuid, Email: currentUser.Email})
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in UpdateUserResetUuid: %v", err)
|
||||
t.Errorf("Error occurred in UpdateUserResetUuid: %v", err)
|
||||
}
|
||||
|
||||
err = dao.ResetUserPassword(models.User{UserId: currentUser.UserId, Password: "HarborTester12345", ResetUuid: uuid, Salt: currentUser.Salt})
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in ResetUserPassword: %v", err)
|
||||
t.Errorf("Error occurred in ResetUserPassword: %v", err)
|
||||
}
|
||||
|
||||
loginedUser, err := dao.LoginByDb(models.AuthModel{Principal: currentUser.Username, Password: "HarborTester12345"})
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in LoginByDb: %v", err)
|
||||
t.Errorf("Error occurred in LoginByDb: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println(loginedUser.Username)
|
||||
//Output:
|
||||
//Tester01
|
||||
if loginedUser.Username != USERNAME {
|
||||
t.Errorf("The username returned by Login does not match, expected: %s, acutal: %s", USERNAME, loginedUser.Username)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleChangeUserPassword() {
|
||||
func TestChangeUserPassword(t *testing.T) {
|
||||
err := dao.ChangeUserPassword(models.User{UserId: currentUser.UserId, Password: "NewHarborTester12345", Salt: currentUser.Salt})
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in ChangeUserPassword: %v", err)
|
||||
t.Errorf("Error occurred in ChangeUserPassword: %v", err)
|
||||
}
|
||||
|
||||
loginedUser, err := dao.LoginByDb(models.AuthModel{Principal: currentUser.Username, Password: "NewHarborTester12345"})
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in LoginByDb: %v", err)
|
||||
t.Errorf("Error occurred in LoginByDb: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println(loginedUser.Username)
|
||||
//Output:
|
||||
//Tester01
|
||||
if loginedUser.Username != USERNAME {
|
||||
t.Errorf("The username returned by Login does not match, expected: %s, acutal: %s", USERNAME, loginedUser.Username)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleQueryRelevantProjectsWhenNoProjectAdded() {
|
||||
func TestChangeUserPasswordWithOldPassword(t *testing.T) {
|
||||
err := dao.ChangeUserPassword(models.User{UserId: currentUser.UserId, Password: "NewerHarborTester12345", Salt: currentUser.Salt}, "NewHarborTester12345")
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in ChangeUserPassword: %v", err)
|
||||
}
|
||||
loginedUser, err := dao.LoginByDb(models.AuthModel{Principal: currentUser.Username, Password: "NewerHarborTester12345"})
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChangeUserPasswordWithIncorrectOldPassword(t *testing.T) {
|
||||
err := dao.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.")
|
||||
}
|
||||
loginedUser, err := dao.LoginByDb(models.AuthModel{Principal: currentUser.Username, Password: "NNewerHarborTester12345"})
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in LoginByDb: %v", err)
|
||||
}
|
||||
if loginedUser != nil {
|
||||
t.Errorf("The login user is not nil, acutal: %+v", loginedUser)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryRelevantProjectsWhenNoProjectAdded(t *testing.T) {
|
||||
projects, err := dao.QueryRelevantProjects(currentUser.UserId)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in QueryRelevantProjects: %v", err)
|
||||
t.Errorf("Error occurred in QueryRelevantProjects: %v", err)
|
||||
}
|
||||
fmt.Println(len(projects))
|
||||
for _, p := range projects {
|
||||
fmt.Println(p.Name)
|
||||
if len(projects) != 1 {
|
||||
t.Errorf("Expected only one project in DB, but actual: %d", len(projects))
|
||||
}
|
||||
if projects[0].Name != "library" {
|
||||
t.Errorf("There name of the project does not match, expected: %s, actual: %s", "library", projects[0].Name)
|
||||
}
|
||||
//Output:
|
||||
//1
|
||||
//library
|
||||
}
|
||||
|
||||
func ExampleAddProject() {
|
||||
func TestAddProject(t *testing.T) {
|
||||
|
||||
project := models.Project{
|
||||
OwnerId: currentUser.UserId,
|
||||
@ -319,134 +355,128 @@ func ExampleAddProject() {
|
||||
|
||||
err := dao.AddProject(project)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in AddProject: %v", err)
|
||||
t.Errorf("Error occurred in AddProject: %v", err)
|
||||
}
|
||||
|
||||
newProject, err := dao.GetProjectByName(PROJECT_NAME)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in GetProjectByName: %v", err)
|
||||
t.Errorf("Error occurred in GetProjectByName: %v", err)
|
||||
}
|
||||
if newProject == nil {
|
||||
log.Fatalf("No project found queried by project name: %v", PROJECT_NAME)
|
||||
t.Errorf("No project found queried by project name: %v", PROJECT_NAME)
|
||||
}
|
||||
fmt.Println(newProject.Name)
|
||||
//Output:
|
||||
//test_project
|
||||
}
|
||||
|
||||
var currentProject *models.Project
|
||||
|
||||
func ExampleGetProject() {
|
||||
func TestGetProject(t *testing.T) {
|
||||
var err error
|
||||
currentProject, err = dao.GetProjectByName(PROJECT_NAME)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in GetProjectByName: %v", err)
|
||||
t.Errorf("Error occurred in GetProjectByName: %v", err)
|
||||
}
|
||||
if currentProject == nil {
|
||||
log.Fatalf("No project found queried by project name: %v", PROJECT_NAME)
|
||||
t.Errorf("No project found queried by project name: %v", PROJECT_NAME)
|
||||
}
|
||||
if currentProject.Name != PROJECT_NAME {
|
||||
t.Errorf("Project name does not match, expected: %s, actual: %s", PROJECT_NAME, currentProject.Name)
|
||||
}
|
||||
fmt.Println(currentProject.Name)
|
||||
//Output:
|
||||
//test_project
|
||||
}
|
||||
|
||||
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
|
||||
from project_role pr
|
||||
left join role r on pr.role_id = r.role_id
|
||||
where project_id = ?`, projectId).QueryRows(&r)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in querying project_role: %v", err)
|
||||
log.Printf("Error occurred in querying project_role: %v", err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func ExampleCheckProjectRoles() {
|
||||
func TestCheckProjectRoles(t *testing.T) {
|
||||
r := getProjectRole(currentProject.ProjectId)
|
||||
fmt.Println(len(r))
|
||||
|
||||
for _, pr := range r {
|
||||
fmt.Println(pr.RoleId, pr.Name)
|
||||
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].Name != "developer" {
|
||||
t.Errorf("The name of role id: 3 should be developer, actual:%s", r[1].Name)
|
||||
}
|
||||
|
||||
//Output: 3
|
||||
//2 projectAdmin
|
||||
//3 developer
|
||||
//4 guest
|
||||
|
||||
}
|
||||
|
||||
func ExampleGetAccessLog() {
|
||||
func TestGetAccessLog(t *testing.T) {
|
||||
queryAccessLog := models.AccessLog{
|
||||
UserId: currentUser.UserId,
|
||||
ProjectId: currentProject.ProjectId,
|
||||
}
|
||||
accessLogs, err := dao.GetAccessLogs(queryAccessLog)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in GetAccessLog: %v", err)
|
||||
t.Errorf("Error occurred in GetAccessLog: %v", err)
|
||||
}
|
||||
fmt.Println(len(accessLogs))
|
||||
for _, log := range accessLogs {
|
||||
fmt.Println(log.Operation, log.RepoName)
|
||||
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)
|
||||
}
|
||||
//Output:
|
||||
//1
|
||||
//create test_project/
|
||||
}
|
||||
|
||||
func ExampleProjectExists() {
|
||||
func TestProjectExists(t *testing.T) {
|
||||
var exists bool
|
||||
var err error
|
||||
exists, err = dao.ProjectExists(currentProject.ProjectId)
|
||||
fmt.Println(exists)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in ProjectExists: %v", err)
|
||||
t.Errorf("Error occurred in ProjectExists: %v", err)
|
||||
}
|
||||
if !exists {
|
||||
t.Errorf("The project with id: %d, does not exist", currentProject.ProjectId)
|
||||
}
|
||||
exists, err = dao.ProjectExists(currentProject.Name)
|
||||
fmt.Println(exists)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in ProjectExists: %v", err)
|
||||
t.Errorf("Error occurred in ProjectExists: %v", err)
|
||||
}
|
||||
if !exists {
|
||||
t.Errorf("The project with name: %s, does not exist", currentProject.Name)
|
||||
}
|
||||
//Output:
|
||||
//true
|
||||
//true
|
||||
|
||||
}
|
||||
|
||||
func ExampleToggleProjectPublicity() {
|
||||
func TestToggleProjectPublicity(t *testing.T) {
|
||||
err := dao.ToggleProjectPublicity(currentProject.ProjectId, PUBLICITY_ON)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in ToggleProjectPublicity: %v", err)
|
||||
t.Errorf("Error occurred in ToggleProjectPublicity: %v", err)
|
||||
}
|
||||
|
||||
currentProject, err = dao.GetProjectByName(PROJECT_NAME)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in GetProjectByName: %v", err)
|
||||
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)
|
||||
}
|
||||
fmt.Println(currentProject.Public)
|
||||
|
||||
err = dao.ToggleProjectPublicity(currentProject.ProjectId, PUBLICITY_OFF)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in ToggleProjectPublicity: %v", err)
|
||||
t.Errorf("Error occurred in ToggleProjectPublicity: %v", err)
|
||||
}
|
||||
|
||||
currentProject, err = dao.GetProjectByName(PROJECT_NAME)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in GetProjectByName: %v", err)
|
||||
t.Errorf("Error occurred in GetProjectByName: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println(currentProject.Public)
|
||||
//Output:
|
||||
//1
|
||||
//0
|
||||
if currentProject.Public != PUBLICITY_OFF {
|
||||
t.Errorf("project, id: %d, its publicity is not off", currentProject.ProjectId)
|
||||
}
|
||||
}
|
||||
|
||||
func getUserProjectRole(projectId int64, userId int) []models.Role {
|
||||
o := orm.NewOrm()
|
||||
var r []models.Role
|
||||
_, err := o.Raw(`select r.role_id, r.name
|
||||
_, 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
|
||||
@ -457,99 +487,83 @@ func getUserProjectRole(projectId int64, userId int) []models.Role {
|
||||
return r
|
||||
}
|
||||
|
||||
func ExampleGetUserProjectRole() {
|
||||
func TestGetUserProjectRole(t *testing.T) {
|
||||
r := getUserProjectRole(currentProject.ProjectId, currentUser.UserId)
|
||||
|
||||
//Get the size of current user project role.
|
||||
fmt.Println(len(r))
|
||||
|
||||
//Iterating current user project role info.
|
||||
for _, upr := range r {
|
||||
fmt.Println(upr.RoleId, upr.Name)
|
||||
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))
|
||||
}
|
||||
|
||||
//Output:
|
||||
//1
|
||||
//2 projectAdmin
|
||||
if r[0].Name != "projectAdmin" {
|
||||
t.Errorf("the expected rolename is: projectAdmin, actual: %s", r[0].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleProjectPermission() {
|
||||
func TestProjectPermission(t *testing.T) {
|
||||
roleCode, err := dao.GetPermission(currentUser.Username, currentProject.Name)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in GetPermission: %v", err)
|
||||
t.Errorf("Error occurred in GetPermission: %v", err)
|
||||
}
|
||||
if roleCode != "MDRWS" {
|
||||
t.Errorf("The expected role code is MDRWS,but actual: %s", roleCode)
|
||||
}
|
||||
fmt.Println(roleCode)
|
||||
//Output:
|
||||
//MDRWS
|
||||
}
|
||||
|
||||
func ExampleQueryRelevantProjects() {
|
||||
func TestQueryRelevantProjects(t *testing.T) {
|
||||
projects, err := dao.QueryRelevantProjects(currentUser.UserId)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in QueryRelevantProjects: %v", err)
|
||||
t.Errorf("Error occurred in QueryRelevantProjects: %v", err)
|
||||
}
|
||||
fmt.Println(len(projects))
|
||||
for _, p := range projects {
|
||||
fmt.Println(p.Name)
|
||||
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)
|
||||
}
|
||||
//Output:
|
||||
//2
|
||||
//library
|
||||
//test_project
|
||||
}
|
||||
|
||||
func ExampleAssignUserProjectRole() {
|
||||
func TestAssignUserProjectRole(t *testing.T) {
|
||||
err := dao.AddUserProjectRole(currentUser.UserId, currentProject.ProjectId, DEVELOPER)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in AddUserProjectRole: %v", err)
|
||||
t.Errorf("Error occurred in AddUserProjectRole: %v", err)
|
||||
}
|
||||
|
||||
r := getUserProjectRole(currentProject.ProjectId, currentUser.UserId)
|
||||
|
||||
//Get the size of current user project role info.
|
||||
fmt.Println(len(r))
|
||||
|
||||
//Iterating current user project role.
|
||||
for _, upr := range r {
|
||||
fmt.Println(upr.RoleId, upr.Name)
|
||||
if len(r) != 2 {
|
||||
t.Errorf("Expected length of role list is 2, actual: %d", len(r))
|
||||
}
|
||||
|
||||
//Output:
|
||||
//2
|
||||
//2 projectAdmin
|
||||
//3 developer
|
||||
if r[1].RoleId != 3 {
|
||||
t.Errorf("Expected role id of the second role in list is 3, actual: %d", r[1].RoleId)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleDeleteUserProjectRole() {
|
||||
func TestDeleteUserProjectRole(t *testing.T) {
|
||||
err := dao.DeleteUserProjectRoles(currentUser.UserId, currentProject.ProjectId)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in DeleteUserProjectRoles: %v", err)
|
||||
t.Errorf("Error occurred in DeleteUserProjectRoles: %v", err)
|
||||
}
|
||||
|
||||
r := getUserProjectRole(currentProject.ProjectId, currentUser.UserId)
|
||||
|
||||
//Get the size of current user project role.
|
||||
fmt.Println(len(r))
|
||||
|
||||
//Iterating current user project role info.
|
||||
for _, upr := range r {
|
||||
fmt.Println(upr.RoleId, upr.Name)
|
||||
if len(r) != 0 {
|
||||
t.Errorf("Expected role list length is 0, actual: %d, role list: %+v", len(r), r)
|
||||
}
|
||||
|
||||
//Output:
|
||||
//0
|
||||
}
|
||||
|
||||
func ExampleDeleteUser() {
|
||||
func TestDeleteUser(t *testing.T) {
|
||||
err := dao.DeleteUser(currentUser.UserId)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in DeleteUser: %v", err)
|
||||
t.Errorf("Error occurred in DeleteUser: %v", err)
|
||||
}
|
||||
user, err := dao.GetUser(*currentUser)
|
||||
if err != nil {
|
||||
log.Fatalf("Error occurred in GetUser: %v", err)
|
||||
t.Errorf("Error occurred in GetUser: %v", err)
|
||||
}
|
||||
if user != nil {
|
||||
t.Error("user is not nil after deletion, user: %+v", user)
|
||||
}
|
||||
fmt.Println(user)
|
||||
//Output:
|
||||
//<nil>
|
||||
}
|
||||
|
@ -55,9 +55,9 @@ func HttpGet(url, sessionId, username, password string) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode == 200 {
|
||||
if response.StatusCode == http.StatusOK {
|
||||
return result, nil
|
||||
} else if response.StatusCode == 401 {
|
||||
} else if response.StatusCode == http.StatusUnauthorized {
|
||||
authenticate := response.Header.Get("WWW-Authenticate")
|
||||
str := strings.Split(authenticate, " ")[1]
|
||||
beego.Trace("url: " + url)
|
||||
@ -106,7 +106,7 @@ func HttpGet(url, sessionId, username, password string) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if response.StatusCode == 200 {
|
||||
if response.StatusCode == http.StatusOK {
|
||||
tt := make(map[string]string)
|
||||
json.Unmarshal(result, &tt)
|
||||
request, err = http.NewRequest("GET", url, nil)
|
||||
|
@ -16,6 +16,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>{{.Hint}}:</p>
|
||||
<a href="{{.Url}}/resetPassword?q={{.Uuid}}">{{.Url}}/resetPassword?q={{.Uuid}}</a>
|
||||
<a href="{{.Url}}/resetPassword?reset_uuid={{.Uuid}}">{{.Url}}/resetPassword?reset_uuid={{.Uuid}}</a>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user