mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-22 18:25:56 +01:00
commit
2319de381d
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,8 +1,5 @@
|
||||
harbor
|
||||
my_start.sh
|
||||
Deploy/config/registry/config.yml
|
||||
Deploy/config/ui/env
|
||||
Deploy/config/ui/app.conf
|
||||
Deploy/config/db/env
|
||||
Deploy/prepare.my
|
||||
Deploy/harbor.cfg.my
|
||||
|
@ -8,6 +8,8 @@ RUN mv /etc/cron.daily/logrotate /etc/cron.hourly/ \
|
||||
&& 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 \
|
||||
&& sed 's/$PrivDropToUser syslog/#$PrivDropToUser syslog/' -i /etc/rsyslog.conf \
|
||||
&& sed 's/$PrivDropToGroup syslog/#$PrivDropToGroup syslog/' -i /etc/rsyslog.conf \
|
||||
&& rm /etc/rsyslog.d/*
|
||||
|
||||
# logrotate configuration file for docker
|
||||
@ -20,5 +22,5 @@ VOLUME /var/log/docker/
|
||||
|
||||
EXPOSE 514
|
||||
|
||||
CMD cron && chown -R syslog:syslog /var/log/docker/ && rsyslogd -n
|
||||
CMD cron && rsyslogd -n
|
||||
|
||||
|
@ -1,24 +0,0 @@
|
||||
FROM ubuntu:14.04
|
||||
|
||||
ENV MYSQL_USR root
|
||||
ENV MYSQL_PWD root
|
||||
ENV MYSQL_PORT_3306_TCP_ADDR localhost
|
||||
ENV MYSQL_PORT_3306_TCP_PORT 3306
|
||||
ENV REGISTRY_URL localhost:5000
|
||||
|
||||
RUN apt-get update -qqy && apt-get install -qqy libldap2-dev
|
||||
|
||||
ADD harbor /go/bin/harbor
|
||||
ADD views /go/bin/views
|
||||
ADD static /go/bin/static
|
||||
|
||||
RUN chmod u+x /go/bin/harbor
|
||||
|
||||
RUN sed -i 's/TLS_CACERT/#TLS_CAERT/g' /etc/ldap/ldap.conf
|
||||
RUN sed -i '$a\TLS_REQCERT allow' /etc/ldap/ldap.conf
|
||||
|
||||
WORKDIR /go/bin/
|
||||
ENTRYPOINT ["/go/bin/harbor"]
|
||||
|
||||
EXPOSE 80
|
||||
|
202
LICENSE
202
LICENSE
@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
1381
License.txt
Normal file
1381
License.txt
Normal file
File diff suppressed because it is too large
Load Diff
11
Notice.txt
Normal file
11
Notice.txt
Normal file
@ -0,0 +1,11 @@
|
||||
Harbor 0.1.0 Beta
|
||||
|
||||
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
You may not use this product except in compliance with the License.
|
||||
|
||||
This product may include a number of subcomponents with
|
||||
separate copyright notices and license terms. Your use of the source
|
||||
code for the these subcomponents is subject to the terms and
|
||||
conditions of the subcomponent's license, as noted in the LICENSE file.
|
@ -22,5 +22,5 @@ $ docker-compose up
|
||||
```
|
||||
|
||||
### License
|
||||
Harbor is available under the [Apache 2 license](LICENSE).
|
||||
Harbor is available under the [Apache 2 license](License.txt).
|
||||
|
||||
|
20
api/base.go
20
api/base.go
@ -12,6 +12,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
@ -24,19 +25,23 @@ import (
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
|
||||
// BaseAPI wraps common methods for controllers to host API
|
||||
type BaseAPI struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
// Render returns nil as it won't render template
|
||||
func (b *BaseAPI) Render() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RenderError provides shortcut to render http error
|
||||
func (b *BaseAPI) RenderError(code int, text string) {
|
||||
http.Error(b.Ctx.ResponseWriter, text, code)
|
||||
}
|
||||
|
||||
func (b *BaseAPI) DecodeJsonReq(v interface{}) {
|
||||
// DecodeJSONReq decodes a json request
|
||||
func (b *BaseAPI) DecodeJSONReq(v interface{}) {
|
||||
err := json.Unmarshal(b.Ctx.Input.CopyBody(1<<32), v)
|
||||
if err != nil {
|
||||
beego.Error("Error while decoding the json request:", err)
|
||||
@ -44,22 +49,23 @@ func (b *BaseAPI) DecodeJsonReq(v interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateUser checks if the request triggered by a valid user
|
||||
func (b *BaseAPI) ValidateUser() int {
|
||||
|
||||
sessionUserId := b.GetSession("userId")
|
||||
if sessionUserId == nil {
|
||||
sessionUserID := b.GetSession("userId")
|
||||
if sessionUserID == nil {
|
||||
beego.Warning("No user id in session, canceling request")
|
||||
b.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
userId := sessionUserId.(int)
|
||||
u, err := dao.GetUser(models.User{UserId: userId})
|
||||
userID := sessionUserID.(int)
|
||||
u, err := dao.GetUser(models.User{UserID: userID})
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUser:", err)
|
||||
b.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if u == nil {
|
||||
beego.Warning("User was deleted already, user id: ", userId, " canceling request.")
|
||||
beego.Warning("User was deleted already, user id: ", userID, " canceling request.")
|
||||
b.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
return userId
|
||||
return userID
|
||||
}
|
||||
|
@ -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(models.Project{ProjectId: pid})
|
||||
p, err := dao.GetProjectByID(pid)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetProjectById:", err)
|
||||
pma.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
@ -55,34 +58,34 @@ 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
|
||||
queryProject := models.Project{ProjectId: pid}
|
||||
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(queryProject, queryUser)
|
||||
userList, err := dao.GetUserByProject(pid, queryUser)
|
||||
if err != nil {
|
||||
beego.Error("Failed to query database for member list, error:", err)
|
||||
pma.RenderError(http.StatusInternalServerError, "Internal Server Error")
|
||||
@ -90,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")
|
||||
@ -194,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
|
||||
}
|
@ -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
|
||||
|
@ -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,25 +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
|
||||
}
|
||||
projectQuery := models.Project{ProjectId: projectId}
|
||||
p, err := dao.GetProjectById(projectQuery)
|
||||
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
|
||||
}
|
||||
@ -103,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
|
||||
}
|
||||
@ -138,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()
|
||||
|
@ -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 {
|
||||
|
44
api/user.go
44
api/user.go
@ -12,6 +12,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
@ -24,48 +25,51 @@ import (
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
|
||||
// UserAPI handles request to /api/users/{}
|
||||
type UserAPI struct {
|
||||
BaseAPI
|
||||
currentUid int
|
||||
userId int
|
||||
currentUserID int
|
||||
userID int
|
||||
}
|
||||
|
||||
// Prepare validates the URL and parms
|
||||
func (ua *UserAPI) Prepare() {
|
||||
|
||||
ua.currentUid = ua.ValidateUser()
|
||||
ua.currentUserID = ua.ValidateUser()
|
||||
id := ua.Ctx.Input.Param(":id")
|
||||
if id == "current" {
|
||||
ua.userId = ua.currentUid
|
||||
ua.userID = ua.currentUserID
|
||||
} else if len(id) > 0 {
|
||||
var err error
|
||||
ua.userId, err = strconv.Atoi(id)
|
||||
ua.userID, err = strconv.Atoi(id)
|
||||
if err != nil {
|
||||
beego.Error("Invalid user id, error:", err)
|
||||
ua.CustomAbort(http.StatusBadRequest, "Invalid user Id")
|
||||
}
|
||||
userQuery := models.User{UserId: ua.userId}
|
||||
userQuery := models.User{UserID: ua.userID}
|
||||
u, err := dao.GetUser(userQuery)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUser:", err)
|
||||
ua.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if u == nil {
|
||||
beego.Error("User with Id:", ua.userId, "does not exist")
|
||||
beego.Error("User with Id:", ua.userID, "does not exist")
|
||||
ua.CustomAbort(http.StatusNotFound, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get ...
|
||||
func (ua *UserAPI) Get() {
|
||||
exist, err := dao.IsAdminRole(ua.currentUid)
|
||||
exist, err := dao.IsAdminRole(ua.currentUserID)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in IsAdminRole:", err)
|
||||
ua.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
|
||||
if ua.userId == 0 { //list users
|
||||
if ua.userID == 0 { //list users
|
||||
if !exist {
|
||||
beego.Error("Current user, id:", ua.currentUid, ", does not have admin role, can not list users")
|
||||
beego.Error("Current user, id:", ua.currentUserID, ", does not have admin role, can not list users")
|
||||
ua.RenderError(http.StatusForbidden, "User does not have admin role")
|
||||
return
|
||||
}
|
||||
@ -82,8 +86,8 @@ func (ua *UserAPI) Get() {
|
||||
}
|
||||
ua.Data["json"] = userList
|
||||
|
||||
} else if ua.userId == ua.currentUid || exist {
|
||||
userQuery := models.User{UserId: ua.userId}
|
||||
} else if ua.userID == ua.currentUserID || exist {
|
||||
userQuery := models.User{UserID: ua.userID}
|
||||
u, err := dao.GetUser(userQuery)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUser:", err)
|
||||
@ -91,40 +95,42 @@ func (ua *UserAPI) Get() {
|
||||
}
|
||||
ua.Data["json"] = u
|
||||
} else {
|
||||
beego.Error("Current user, id:", ua.currentUid, "does not have admin role, can not view other user's detail")
|
||||
beego.Error("Current user, id:", ua.currentUserID, "does not have admin role, can not view other user's detail")
|
||||
ua.RenderError(http.StatusForbidden, "User does not have admin role")
|
||||
return
|
||||
}
|
||||
ua.ServeJSON()
|
||||
}
|
||||
|
||||
// Put ...
|
||||
func (ua *UserAPI) Put() { //currently only for toggle admin, so no request body
|
||||
exist, err := dao.IsAdminRole(ua.currentUid)
|
||||
exist, err := dao.IsAdminRole(ua.currentUserID)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in IsAdminRole:", err)
|
||||
ua.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if !exist {
|
||||
beego.Warning("current user, id:", ua.currentUid, ", does not have admin role, can not update other user's role")
|
||||
beego.Warning("current user, id:", ua.currentUserID, ", does not have admin role, can not update other user's role")
|
||||
ua.RenderError(http.StatusForbidden, "User does not have admin role")
|
||||
return
|
||||
}
|
||||
userQuery := models.User{UserId: ua.userId}
|
||||
userQuery := models.User{UserID: ua.userID}
|
||||
dao.ToggleUserAdminRole(userQuery)
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
func (ua *UserAPI) Delete() {
|
||||
exist, err := dao.IsAdminRole(ua.currentUid)
|
||||
exist, err := dao.IsAdminRole(ua.currentUserID)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in IsAdminRole:", err)
|
||||
ua.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if !exist {
|
||||
beego.Warning("current user, id:", ua.currentUid, ", does not have admin role, can not remove user")
|
||||
beego.Warning("current user, id:", ua.currentUserID, ", does not have admin role, can not remove user")
|
||||
ua.RenderError(http.StatusForbidden, "User does not have admin role")
|
||||
return
|
||||
}
|
||||
err = dao.DeleteUser(ua.userId)
|
||||
err = dao.DeleteUser(ua.userID)
|
||||
if err != nil {
|
||||
beego.Error("Failed to delete data from database, error:", err)
|
||||
ua.RenderError(http.StatusInternalServerError, "Failed to delete User")
|
||||
|
11
api/utils.go
11
api/utils.go
@ -12,6 +12,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
@ -21,8 +22,8 @@ import (
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
|
||||
func CheckProjectPermission(userId int, projectId int64) bool {
|
||||
exist, err := dao.IsAdminRole(userId)
|
||||
func checkProjectPermission(userID int, projectID int64) bool {
|
||||
exist, err := dao.IsAdminRole(userID)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in IsAdminRole:", err)
|
||||
return false
|
||||
@ -30,7 +31,7 @@ func CheckProjectPermission(userId int, projectId int64) bool {
|
||||
if exist {
|
||||
return true
|
||||
}
|
||||
roleList, err := dao.GetUserProjectRoles(models.User{UserId: userId}, projectId)
|
||||
roleList, err := dao.GetUserProjectRoles(models.User{UserID: userID}, projectID)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUserProjectRoles:", err)
|
||||
return false
|
||||
@ -38,14 +39,14 @@ func CheckProjectPermission(userId int, projectId int64) bool {
|
||||
return len(roleList) > 0
|
||||
}
|
||||
|
||||
func CheckUserExists(name string) int {
|
||||
func checkUserExists(name string) int {
|
||||
u, err := dao.GetUser(models.User{Username: name})
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUser:", err)
|
||||
return 0
|
||||
}
|
||||
if u != nil {
|
||||
return u.UserId
|
||||
return u.UserID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
@ -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{})
|
||||
}
|
@ -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{})
|
||||
}
|
@ -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"
|
||||
|
@ -12,6 +12,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
@ -25,55 +26,24 @@ import (
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
|
||||
// ItemDetailController handles requet to /registry/detail, which shows the detail of a project.
|
||||
type ItemDetailController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
var SYS_ADMIN int = 1
|
||||
var PROJECT_ADMIN int = 2
|
||||
var DEVELOPER int = 3
|
||||
var GUEST int = 4
|
||||
|
||||
func CheckProjectRole(userId int, projectId int64) bool {
|
||||
if projectId == 0 {
|
||||
return false
|
||||
}
|
||||
userQuery := models.User{UserId: int(userId)}
|
||||
if userId == SYS_ADMIN {
|
||||
return true
|
||||
}
|
||||
roleList, err := dao.GetUserProjectRoles(userQuery, projectId)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetUserProjectRoles:", err)
|
||||
return false
|
||||
}
|
||||
return len(roleList) > 0
|
||||
}
|
||||
|
||||
func CheckPublicProject(projectId int64) bool {
|
||||
projectQuery := models.Project{ProjectId: projectId}
|
||||
project, err := dao.GetProjectById(projectQuery)
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetProjectById:", err)
|
||||
return false
|
||||
}
|
||||
if project != nil && project.Public == 1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 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() {
|
||||
|
||||
sessionUserId := idc.GetSession("userId")
|
||||
projectId, _ := idc.GetInt64("project_id")
|
||||
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()), http.StatusFound)
|
||||
if projectID <= 0 {
|
||||
beego.Error("Invalid project id:", projectID)
|
||||
idc.Redirect("/signIn", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
projectQuery := models.Project{ProjectId: projectId}
|
||||
project, err := dao.GetProjectById(projectQuery)
|
||||
project, err := dao.GetProjectByID(projectID)
|
||||
|
||||
if err != nil {
|
||||
beego.Error("Error occurred in GetProjectById:", err)
|
||||
@ -82,26 +52,42 @@ func (idc *ItemDetailController) Get() {
|
||||
|
||||
if project == nil {
|
||||
idc.Redirect("/signIn", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
idc.Data["ProjectId"] = project.ProjectId
|
||||
idc.Data["ProjectName"] = project.Name
|
||||
idc.Data["OwnerName"] = project.OwnerName
|
||||
idc.Data["OwnerId"] = project.OwnerId
|
||||
sessionUserID := idc.GetSession("userId")
|
||||
|
||||
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)
|
||||
roleList, err := dao.GetUserProjectRoles(models.User{UserId: sessionUserId.(int)}, projectId)
|
||||
idc.Data["UserId"] = sessionUserID.(int)
|
||||
|
||||
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.")
|
||||
}
|
||||
|
||||
if project.Public == 0 && len(roleList) == 0 {
|
||||
idc.Redirect("/registry/project", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
if len(roleList) > 0 {
|
||||
idc.Data["RoleId"] = roleList[0].RoleId
|
||||
idc.Data["RoleId"] = roleList[0].RoleID
|
||||
}
|
||||
}
|
||||
|
||||
idc.Data["ProjectId"] = project.ProjectID
|
||||
idc.Data["ProjectName"] = project.Name
|
||||
idc.Data["OwnerName"] = project.OwnerName
|
||||
idc.Data["OwnerId"] = project.OwnerID
|
||||
|
||||
idc.Data["HarborRegUrl"] = os.Getenv("HARBOR_REG_URL")
|
||||
idc.Data["RepoName"] = idc.GetString("repo_name")
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
@ -28,160 +29,170 @@ 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
|
||||
}
|
||||
cpc.Data["Username"] = cpc.GetSession("username")
|
||||
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)
|
||||
@ -189,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")
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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) " +
|
32
dao/base.go
32
dao/base.go
@ -12,6 +12,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
@ -22,12 +23,12 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/orm"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/go-sql-driver/mysql" //register mysql driver
|
||||
)
|
||||
|
||||
const NON_EXIST_USER_ID = 0
|
||||
// NonExistUserID : if a user does not exist, the ID of the user will be 0.
|
||||
const NonExistUserID = 0
|
||||
|
||||
func isIllegalLength(s string, min int, max int) bool {
|
||||
if min == -1 {
|
||||
@ -48,6 +49,7 @@ func isContainIllegalChar(s string, illegalChar []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GenerateRandomString generates a random string
|
||||
func GenerateRandomString() (string, error) {
|
||||
o := orm.NewOrm()
|
||||
var uuid string
|
||||
@ -59,6 +61,7 @@ func GenerateRandomString() (string, error) {
|
||||
|
||||
}
|
||||
|
||||
//InitDB initializes the database
|
||||
func InitDB() {
|
||||
orm.RegisterDriver("mysql", orm.DRMySQL)
|
||||
addr := os.Getenv("MYSQL_HOST")
|
||||
@ -74,26 +77,7 @@ func InitDB() {
|
||||
password = os.Getenv("MYSQL_PWD")
|
||||
}
|
||||
|
||||
var flag bool = true
|
||||
if addr == "" {
|
||||
beego.Error("Unset env of MYSQL_HOST")
|
||||
flag = false
|
||||
} else if port == "" {
|
||||
beego.Error("Unset env of MYSQL_PORT_3306_TCP_PORT")
|
||||
flag = false
|
||||
} else if username == "" {
|
||||
beego.Error("Unset env of MYSQL_USR")
|
||||
flag = false
|
||||
} else if password == "" {
|
||||
beego.Error("Unset env of MYSQL_PWD")
|
||||
flag = false
|
||||
}
|
||||
|
||||
if !flag {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
db_str := username + ":" + password + "@tcp(" + addr + ":" + port + ")/registry"
|
||||
dbStr := username + ":" + password + "@tcp(" + addr + ":" + port + ")/registry"
|
||||
ch := make(chan int, 1)
|
||||
go func() {
|
||||
var err error
|
||||
@ -114,7 +98,7 @@ func InitDB() {
|
||||
case <-time.After(60 * time.Second):
|
||||
panic("Failed to connect to DB after 60 seconds")
|
||||
}
|
||||
err := orm.RegisterDataBase("default", "mysql", db_str)
|
||||
err := orm.RegisterDataBase("default", "mysql", dbStr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -12,17 +12,16 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package test
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
// "fmt"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
|
||||
"github.com/astaxie/beego/orm"
|
||||
@ -84,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) {
|
||||
|
||||
@ -109,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,8 +116,8 @@ func TestMain(m *testing.M) {
|
||||
os.Setenv("MYSQL_USR", dbUser)
|
||||
os.Setenv("MYSQL_PWD", dbPassword)
|
||||
os.Setenv("AUTH_MODE", "db_auth")
|
||||
dao.InitDB()
|
||||
clearUp(USERNAME)
|
||||
InitDB()
|
||||
clearUp(username)
|
||||
os.Exit(m.Run())
|
||||
|
||||
}
|
||||
@ -129,29 +125,29 @@ 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",
|
||||
Comment: "register",
|
||||
}
|
||||
|
||||
_, err := dao.Register(user)
|
||||
_, err := Register(user)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in Register: %v", err)
|
||||
}
|
||||
|
||||
//Check if user registered successfully.
|
||||
queryUser := models.User{
|
||||
Username: USERNAME,
|
||||
Username: username,
|
||||
}
|
||||
newUser, err := dao.GetUser(queryUser)
|
||||
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)
|
||||
@ -162,14 +158,14 @@ func TestUserExists(t *testing.T) {
|
||||
var exists bool
|
||||
var err error
|
||||
|
||||
exists, err = dao.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 = dao.UserExists(models.User{Email: "tester01@vmware.com"}, "email")
|
||||
exists, err = UserExists(models.User{Email: "tester01@vmware.com"}, "email")
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in UserExists: %v", err)
|
||||
@ -177,7 +173,7 @@ func TestUserExists(t *testing.T) {
|
||||
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")
|
||||
exists, err = UserExists(models.User{Username: "NOTHERE"}, "username")
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in UserExists: %v", err)
|
||||
}
|
||||
@ -189,11 +185,11 @@ func TestUserExists(t *testing.T) {
|
||||
func TestLoginByUserName(t *testing.T) {
|
||||
|
||||
userQuery := models.User{
|
||||
Username: USERNAME,
|
||||
Username: username,
|
||||
Password: "Abc12345",
|
||||
}
|
||||
|
||||
loginUser, err := dao.LoginByDb(models.AuthModel{userQuery.Username, userQuery.Password})
|
||||
loginUser, err := LoginByDb(models.AuthModel{userQuery.Username, userQuery.Password})
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in LoginByDb: %v", err)
|
||||
}
|
||||
@ -201,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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,15 +209,15 @@ func TestLoginByEmail(t *testing.T) {
|
||||
Password: "Abc12345",
|
||||
}
|
||||
|
||||
loginUser, err := dao.LoginByDb(models.AuthModel{userQuery.Email, userQuery.Password})
|
||||
loginUser, err := LoginByDb(models.AuthModel{userQuery.Email, userQuery.Password})
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in LoginByDb: %v", err)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,10 +225,10 @@ var currentUser *models.User
|
||||
|
||||
func TestGetUser(t *testing.T) {
|
||||
queryUser := models.User{
|
||||
Username: USERNAME,
|
||||
Username: username,
|
||||
}
|
||||
var err error
|
||||
currentUser, err = dao.GetUser(queryUser)
|
||||
currentUser, err = GetUser(queryUser)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in GetUser: %v", err)
|
||||
}
|
||||
@ -245,84 +241,84 @@ func TestGetUser(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListUsers(t *testing.T) {
|
||||
users, err := dao.ListUsers(models.User{})
|
||||
users, err := ListUsers(models.User{})
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in ListUsers: %v", err)
|
||||
}
|
||||
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})
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetUserPassword(t *testing.T) {
|
||||
uuid, err := dao.GenerateRandomString()
|
||||
uuid, err := GenerateRandomString()
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in GenerateRandomString: %v", err)
|
||||
}
|
||||
|
||||
err = dao.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 = dao.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)
|
||||
}
|
||||
|
||||
loginedUser, err := dao.LoginByDb(models.AuthModel{Principal: currentUser.Username, Password: "HarborTester12345"})
|
||||
loginedUser, err := LoginByDb(models.AuthModel{Principal: currentUser.Username, Password: "HarborTester12345"})
|
||||
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 TestChangeUserPassword(t *testing.T) {
|
||||
err := dao.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)
|
||||
}
|
||||
|
||||
loginedUser, err := dao.LoginByDb(models.AuthModel{Principal: currentUser.Username, Password: "NewHarborTester12345"})
|
||||
loginedUser, err := LoginByDb(models.AuthModel{Principal: currentUser.Username, Password: "NewHarborTester12345"})
|
||||
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 TestChangeUserPasswordWithOldPassword(t *testing.T) {
|
||||
err := dao.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)
|
||||
}
|
||||
loginedUser, err := dao.LoginByDb(models.AuthModel{Principal: currentUser.Username, Password: "NewerHarborTester12345"})
|
||||
loginedUser, err := 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)
|
||||
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")
|
||||
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.")
|
||||
}
|
||||
loginedUser, err := dao.LoginByDb(models.AuthModel{Principal: currentUser.Username, Password: "NNewerHarborTester12345"})
|
||||
loginedUser, err := LoginByDb(models.AuthModel{Principal: currentUser.Username, Password: "NNewerHarborTester12345"})
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in LoginByDb: %v", err)
|
||||
}
|
||||
@ -332,7 +328,7 @@ func TestChangeUserPasswordWithIncorrectOldPassword(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestQueryRelevantProjectsWhenNoProjectAdded(t *testing.T) {
|
||||
projects, err := dao.QueryRelevantProjects(currentUser.UserId)
|
||||
projects, err := QueryRelevantProjects(currentUser.UserID)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in QueryRelevantProjects: %v", err)
|
||||
}
|
||||
@ -347,23 +343,23 @@ 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,
|
||||
}
|
||||
|
||||
err := dao.AddProject(project)
|
||||
err := AddProject(project)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in AddProject: %v", err)
|
||||
}
|
||||
|
||||
newProject, err := dao.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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -371,25 +367,25 @@ var currentProject *models.Project
|
||||
|
||||
func TestGetProject(t *testing.T) {
|
||||
var err error
|
||||
currentProject, err = dao.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)
|
||||
}
|
||||
@ -397,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)
|
||||
@ -411,32 +407,32 @@ 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 := dao.GetAccessLogs(queryAccessLog)
|
||||
accessLogs, err := GetAccessLogs(queryAccessLog)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in GetAccessLog: %v", err)
|
||||
}
|
||||
if len(accessLogs) != 1 {
|
||||
t.Errorf("The length of accesslog list should be 1, actual: %d", len(accessLogs))
|
||||
}
|
||||
if accessLogs[0].RepoName != 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 = dao.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 = dao.ProjectExists(currentProject.Name)
|
||||
exists, err = ProjectExists(currentProject.Name)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in ProjectExists: %v", err)
|
||||
}
|
||||
@ -445,63 +441,154 @@ func TestProjectExists(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestToggleProjectPublicity(t *testing.T) {
|
||||
err := dao.ToggleProjectPublicity(currentProject.ProjectId, PUBLICITY_ON)
|
||||
func TestGetProjectById(t *testing.T) {
|
||||
id := currentProject.ProjectID
|
||||
p, err := GetProjectByID(id)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in ToggleProjectPublicity: %v", err)
|
||||
t.Errorf("Error in GetProjectById: %v, id: %d", err, id)
|
||||
}
|
||||
|
||||
currentProject, err = dao.GetProjectByName(PROJECT_NAME)
|
||||
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)
|
||||
}
|
||||
err = dao.ToggleProjectPublicity(currentProject.ProjectId, PUBLICITY_OFF)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in ToggleProjectPublicity: %v", err)
|
||||
}
|
||||
|
||||
currentProject, err = dao.GetProjectByName(PROJECT_NAME)
|
||||
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 p.Name != currentProject.Name {
|
||||
t.Errorf("project name does not match, expected: %s, actual: %s", currentProject.Name, p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func getUserProjectRole(projectId int64, userId int) []models.Role {
|
||||
func TestGetUserByProject(t *testing.T) {
|
||||
pid := currentProject.ProjectID
|
||||
u1 := models.User{
|
||||
Username: "%%Tester%%",
|
||||
}
|
||||
u2 := models.User{
|
||||
Username: "nononono",
|
||||
}
|
||||
users, err := GetUserByProject(pid, u1)
|
||||
if err != nil {
|
||||
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", err, pid, u2)
|
||||
}
|
||||
if len(users) != 0 {
|
||||
t.Errorf("unexpected length of user list, expected: 0, the users list: %+v", users)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestToggleProjectPublicity(t *testing.T) {
|
||||
err := ToggleProjectPublicity(currentProject.ProjectID, publicityOn)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in ToggleProjectPublicity: %v", err)
|
||||
}
|
||||
|
||||
currentProject, err = GetProjectByName(projectName)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in GetProjectByName: %v", err)
|
||||
}
|
||||
if currentProject.Public != publicityOn {
|
||||
t.Errorf("project, id: %d, its publicity is not on", currentProject.ProjectID)
|
||||
}
|
||||
err = ToggleProjectPublicity(currentProject.ProjectID, publicityOff)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in ToggleProjectPublicity: %v", err)
|
||||
}
|
||||
|
||||
currentProject, err = GetProjectByName(projectName)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in GetProjectByName: %v", err)
|
||||
}
|
||||
|
||||
if currentProject.Public != publicityOff {
|
||||
t.Errorf("project, id: %d, its publicity is not off", currentProject.ProjectID)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestIsProjectPublic(t *testing.T) {
|
||||
|
||||
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,
|
||||
}
|
||||
projects, err := QueryProject(query1)
|
||||
if err != nil {
|
||||
t.Errorf("Error in Query Project: %v, query: %+v", err, query1)
|
||||
}
|
||||
if len(projects) != 2 {
|
||||
t.Errorf("Expecting get 2 projects, but actual: %d, the list: %+v", len(projects), projects)
|
||||
}
|
||||
query2 := models.Project{
|
||||
Public: 1,
|
||||
}
|
||||
projects, err = QueryProject(query2)
|
||||
if err != nil {
|
||||
t.Errorf("Error in Query Project: %v, query: %+v", err, query2)
|
||||
}
|
||||
if len(projects) != 1 {
|
||||
t.Errorf("Expecting get 1 project, but actual: %d, the list: %+v", len(projects), projects)
|
||||
}
|
||||
query3 := models.Project{
|
||||
UserID: 9,
|
||||
}
|
||||
projects, err = QueryProject(query3)
|
||||
if err != nil {
|
||||
t.Errorf("Error in Query Project: %v, query: %+v", err, query3)
|
||||
}
|
||||
if len(projects) != 0 {
|
||||
t.Errorf("Expecting get 0 project, but actual: %d, the list: %+v", len(projects), projects)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func TestGetUserProjectRole(t *testing.T) {
|
||||
r := getUserProjectRole(currentProject.ProjectId, currentUser.UserId)
|
||||
func TestGetUserProjectRoles(t *testing.T) {
|
||||
user := *currentUser
|
||||
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)
|
||||
}
|
||||
|
||||
//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
|
||||
|
||||
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)
|
||||
}
|
||||
//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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProjectPermission(t *testing.T) {
|
||||
roleCode, err := dao.GetPermission(currentUser.Username, currentProject.Name)
|
||||
roleCode, err := GetPermission(currentUser.Username, currentProject.Name)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in GetPermission: %v", err)
|
||||
}
|
||||
@ -511,59 +598,84 @@ func TestProjectPermission(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestQueryRelevantProjects(t *testing.T) {
|
||||
projects, err := dao.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 := dao.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 := dao.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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToggleAdminRole(t *testing.T) {
|
||||
err := ToggleUserAdminRole(*currentUser)
|
||||
if err != nil {
|
||||
t.Errorf("Error in toggle ToggleUserAdmin role: %v, user: %+v", err, currentUser)
|
||||
}
|
||||
isAdmin, err := IsAdminRole(currentUser.UserID)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
err = ToggleUserAdminRole(*currentUser)
|
||||
if err != nil {
|
||||
t.Errorf("Error in toggle ToggleUserAdmin role: %v, user: %+v", err, currentUser)
|
||||
}
|
||||
isAdmin, err = IsAdminRole(currentUser.UserID)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteUser(t *testing.T) {
|
||||
err := dao.DeleteUser(currentUser.UserId)
|
||||
err := DeleteUser(currentUser.UserID)
|
||||
if err != nil {
|
||||
t.Errorf("Error occurred in DeleteUser: %v", err)
|
||||
}
|
||||
user, err := dao.GetUser(*currentUser)
|
||||
user, err := GetUser(*currentUser)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
@ -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(queryProject models.Project, 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,14 +37,11 @@ func GetUserByProject(queryProject models.Project, queryUser models.User) ([]mod
|
||||
and pr.project_id = ? `
|
||||
|
||||
queryParam := make([]interface{}, 1)
|
||||
queryParam = append(queryParam, queryProject.ProjectId)
|
||||
queryParam = append(queryParam, projectID)
|
||||
|
||||
if queryUser.Username != "" {
|
||||
sql += " and u.username like ? "
|
||||
queryParam = append(queryParam, queryUser.Username)
|
||||
} else if queryUser.RoleId != 0 {
|
||||
sql += ` and r.role_id <= ? `
|
||||
queryParam = append(queryParam, queryUser.RoleId)
|
||||
}
|
||||
sql += ` order by u.user_id `
|
||||
_, err := o.Raw(sql, queryParam).QueryRows(&u)
|
@ -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,6 +98,7 @@ func IsProjectPublic(projectName string) bool {
|
||||
return project.Public == 1
|
||||
}
|
||||
|
||||
// QueryProject querys the projects based on publicity and user, disregarding the names etc.
|
||||
func QueryProject(query models.Project) ([]models.Project, error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
@ -110,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 != "" {
|
||||
@ -132,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
|
||||
}
|
||||
@ -154,17 +160,14 @@ func ProjectExists(nameOrId interface{}) (bool, error) {
|
||||
|
||||
}
|
||||
|
||||
func GetProjectById(query models.Project) (*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, query.ProjectId)
|
||||
if query.Public != 0 {
|
||||
sql += " and p.public = ? "
|
||||
queryParam = append(queryParam, query.Public)
|
||||
}
|
||||
queryParam = append(queryParam, projectID)
|
||||
|
||||
p := []models.Project{}
|
||||
count, err := o.Raw(sql, queryParam).QueryRows(&p)
|
||||
@ -178,6 +181,7 @@ func GetProjectById(query models.Project) (*models.Project, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetProjectByName ...
|
||||
func GetProjectByName(projectName string) (*models.Project, error) {
|
||||
o := orm.NewOrm()
|
||||
var p []models.Project
|
||||
@ -191,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()
|
||||
|
||||
@ -212,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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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 == "" {
|
||||
|
19
dao/role.go
19
dao/role.go
@ -12,6 +12,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
@ -20,7 +21,8 @@ import (
|
||||
"github.com/astaxie/beego/orm"
|
||||
)
|
||||
|
||||
func GetUserProjectRoles(userQuery models.User, projectId int64) ([]models.Role, error) {
|
||||
// GetUserProjectRoles returns roles that the user has according to the project.
|
||||
func GetUserProjectRoles(userQuery models.User, projectID int64) ([]models.Role, error) {
|
||||
|
||||
o := orm.NewOrm()
|
||||
|
||||
@ -32,15 +34,15 @@ func GetUserProjectRoles(userQuery models.User, projectId int64) ([]models.Role,
|
||||
where u.deleted = 0
|
||||
and u.user_id = ? `
|
||||
queryParam := make([]interface{}, 1)
|
||||
queryParam = append(queryParam, userQuery.UserId)
|
||||
queryParam = append(queryParam, userQuery.UserID)
|
||||
|
||||
if projectId > 0 {
|
||||
if projectID > 0 {
|
||||
sql += ` and pr.project_id = ? `
|
||||
queryParam = append(queryParam, projectId)
|
||||
queryParam = append(queryParam, projectID)
|
||||
}
|
||||
if userQuery.RoleId > 0 {
|
||||
if userQuery.RoleID > 0 {
|
||||
sql += ` and r.role_id = ? `
|
||||
queryParam = append(queryParam, userQuery.RoleId)
|
||||
queryParam = append(queryParam, userQuery.RoleID)
|
||||
}
|
||||
|
||||
var roleList []models.Role
|
||||
@ -52,9 +54,10 @@ func GetUserProjectRoles(userQuery models.User, projectId int64) ([]models.Role,
|
||||
return roleList, nil
|
||||
}
|
||||
|
||||
func IsAdminRole(userId int) (bool, error) {
|
||||
// IsAdminRole returns whether the user is admin.
|
||||
func IsAdminRole(userID int) (bool, error) {
|
||||
//role_id == 1 means the user is system admin
|
||||
userQuery := models.User{UserId: userId, RoleId: models.SYSADMIN}
|
||||
userQuery := models.User{UserID: userID, RoleID: models.SYSADMIN}
|
||||
adminRoleList, err := GetUserProjectRoles(userQuery, 0)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
42
dao/user.go
42
dao/user.go
@ -12,6 +12,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
@ -25,6 +26,7 @@ import (
|
||||
"github.com/astaxie/beego/orm"
|
||||
)
|
||||
|
||||
// GetUser ...
|
||||
func GetUser(query models.User) (*models.User, error) {
|
||||
|
||||
o := orm.NewOrm()
|
||||
@ -38,9 +40,9 @@ func GetUser(query models.User) (*models.User, error) {
|
||||
from user u
|
||||
where deleted = 0 `
|
||||
queryParam := make([]interface{}, 1)
|
||||
if query.UserId != 0 {
|
||||
if query.UserID != 0 {
|
||||
sql += ` and user_id = ? `
|
||||
queryParam = append(queryParam, query.UserId)
|
||||
queryParam = append(queryParam, query.UserID)
|
||||
}
|
||||
|
||||
if query.Username != "" {
|
||||
@ -48,9 +50,9 @@ func GetUser(query models.User) (*models.User, error) {
|
||||
queryParam = append(queryParam, query.Username)
|
||||
}
|
||||
|
||||
if query.ResetUuid != "" {
|
||||
if query.ResetUUID != "" {
|
||||
sql += ` and reset_uuid = ? `
|
||||
queryParam = append(queryParam, query.ResetUuid)
|
||||
queryParam = append(queryParam, query.ResetUUID)
|
||||
}
|
||||
|
||||
var u []models.User
|
||||
@ -65,6 +67,7 @@ func GetUser(query models.User) (*models.User, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// LoginByDb is used for user to login with database auth mode.
|
||||
func LoginByDb(auth models.AuthModel) (*models.User, error) {
|
||||
|
||||
query := models.User{Username: auth.Principal, Email: auth.Principal}
|
||||
@ -84,6 +87,7 @@ func LoginByDb(auth models.AuthModel) (*models.User, error) {
|
||||
|
||||
}
|
||||
|
||||
// ListUsers lists all users according to different conditions.
|
||||
func ListUsers(query models.User) ([]models.User, error) {
|
||||
o := orm.NewOrm()
|
||||
u := []models.User{}
|
||||
@ -106,15 +110,16 @@ func ListUsers(query models.User) ([]models.User, error) {
|
||||
return u, err
|
||||
}
|
||||
|
||||
// ToggleUserAdminRole gives a user admim role.
|
||||
func ToggleUserAdminRole(u models.User) error {
|
||||
|
||||
projectRole := models.ProjectRole{PrId: 1} //admin project role
|
||||
projectRole := models.ProjectRole{PrID: 1} //admin project role
|
||||
|
||||
o := orm.NewOrm()
|
||||
|
||||
var pr []models.ProjectRole
|
||||
|
||||
n, err := o.Raw(`select user_id from user_project_role where user_id = ? and pr_id = ? `, u.UserId, projectRole.PrId).QueryRows(&pr)
|
||||
n, err := o.Raw(`select user_id from user_project_role where user_id = ? and pr_id = ? `, u.UserID, projectRole.PrID).QueryRows(&pr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -131,20 +136,21 @@ func ToggleUserAdminRole(u models.User) error {
|
||||
return err
|
||||
}
|
||||
defer p.Close()
|
||||
_, err = p.Exec(u.UserId, projectRole.PrId)
|
||||
_, err = p.Exec(u.UserID, projectRole.PrID)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// ChangeUserPassword ...
|
||||
func ChangeUserPassword(u models.User, oldPassword ...string) error {
|
||||
o := orm.NewOrm()
|
||||
var err error
|
||||
var r sql.Result
|
||||
if len(oldPassword) == 0 {
|
||||
//In some cases, it may no need to check old password, just as Linux change password policies.
|
||||
_, err = o.Raw(`update user set password=?, salt=? where user_id=?`, utils.Encrypt(u.Password, u.Salt), u.Salt, u.UserId).Exec()
|
||||
_, err = o.Raw(`update user set password=?, salt=? where user_id=?`, utils.Encrypt(u.Password, u.Salt), u.Salt, u.UserID).Exec()
|
||||
} else if len(oldPassword) == 1 {
|
||||
r, err = o.Raw(`update user set password=?, salt=? where user_id=? and password = ?`, utils.Encrypt(u.Password, u.Salt), u.Salt, u.UserId, utils.Encrypt(oldPassword[0], u.Salt)).Exec()
|
||||
r, err = o.Raw(`update user set password=?, salt=? where user_id=? and password = ?`, utils.Encrypt(u.Password, u.Salt), u.Salt, u.UserID, utils.Encrypt(oldPassword[0], u.Salt)).Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -161,9 +167,10 @@ func ChangeUserPassword(u models.User, oldPassword ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// ResetUserPassword ...
|
||||
func ResetUserPassword(u models.User) error {
|
||||
o := orm.NewOrm()
|
||||
r, err := o.Raw(`update user set password=?, reset_uuid=? where reset_uuid=?`, utils.Encrypt(u.Password, u.Salt), "", u.ResetUuid).Exec()
|
||||
r, err := o.Raw(`update user set password=?, reset_uuid=? where reset_uuid=?`, utils.Encrypt(u.Password, u.Salt), "", u.ResetUUID).Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -177,12 +184,14 @@ func ResetUserPassword(u models.User) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdateUserResetUuid(u models.User) error {
|
||||
// UpdateUserResetUUID ...
|
||||
func UpdateUserResetUUID(u models.User) error {
|
||||
o := orm.NewOrm()
|
||||
_, err := o.Raw(`update user set reset_uuid=? where email=?`, u.ResetUuid, u.Email).Exec()
|
||||
_, err := o.Raw(`update user set reset_uuid=? where email=?`, u.ResetUUID, u.Email).Exec()
|
||||
return err
|
||||
}
|
||||
|
||||
// CheckUserPassword checks whether the password is correct.
|
||||
func CheckUserPassword(query models.User) (*models.User, error) {
|
||||
|
||||
currentUser, err := GetUser(query)
|
||||
@ -199,10 +208,10 @@ func CheckUserPassword(query models.User) (*models.User, error) {
|
||||
|
||||
queryParam := make([]interface{}, 1)
|
||||
|
||||
if query.UserId != 0 {
|
||||
if query.UserID != 0 {
|
||||
sql += ` and password = ? and user_id = ?`
|
||||
queryParam = append(queryParam, utils.Encrypt(query.Password, currentUser.Salt))
|
||||
queryParam = append(queryParam, query.UserId)
|
||||
queryParam = append(queryParam, query.UserID)
|
||||
} else {
|
||||
sql += ` and username = ? and password = ?`
|
||||
queryParam = append(queryParam, currentUser.Username)
|
||||
@ -223,8 +232,9 @@ func CheckUserPassword(query models.User) (*models.User, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteUser(userId int) error {
|
||||
// DeleteUser ...
|
||||
func DeleteUser(userID int) error {
|
||||
o := orm.NewOrm()
|
||||
_, err := o.Raw(`update user set deleted = 1 where user_id = ?`, userId).Exec()
|
||||
_, err := o.Raw(`update user set deleted = 1 where user_id = ?`, userID).Exec()
|
||||
return err
|
||||
}
|
||||
|
26
main.go
26
main.go
@ -12,17 +12,17 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
_ "github.com/vmware/harbor/auth/db"
|
||||
_ "github.com/vmware/harbor/auth/ldap"
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
_ "github.com/vmware/harbor/opt_auth/db"
|
||||
_ "github.com/vmware/harbor/opt_auth/ldap"
|
||||
_ "github.com/vmware/harbor/routers"
|
||||
|
||||
"os"
|
||||
@ -31,19 +31,19 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
ADMIN_USER_ID = 1
|
||||
adminUserID = 1
|
||||
)
|
||||
|
||||
func updateInitPassword(userId int, password string) error {
|
||||
queryUser := models.User{UserId: userId}
|
||||
func updateInitPassword(userID int, password string) error {
|
||||
queryUser := models.User{UserID: userID}
|
||||
user, err := dao.GetUser(queryUser)
|
||||
if err != nil {
|
||||
log.Println("Failed to get user, userId:", userId)
|
||||
log.Println("Failed to get user, userID:", userID)
|
||||
return err
|
||||
}
|
||||
if user == nil {
|
||||
log.Printf("User id: %d does not exist.", userId)
|
||||
return errors.New(fmt.Sprintf("User id: %s does not exist.", userId))
|
||||
log.Printf("User id: %d does not exist.", userID)
|
||||
return fmt.Errorf("User id: %d does not exist.", userID)
|
||||
} else if user.Salt == "" {
|
||||
salt, err := dao.GenerateRandomString()
|
||||
if err != nil {
|
||||
@ -54,12 +54,12 @@ func updateInitPassword(userId int, password string) error {
|
||||
user.Password = password
|
||||
err = dao.ChangeUserPassword(*user)
|
||||
if err != nil {
|
||||
log.Printf("Failed to update user encrypted password, userId: %d, err: %v", userId, err)
|
||||
log.Printf("Failed to update user encrypted password, userID: %d, err: %v", userID, err)
|
||||
return err
|
||||
}
|
||||
log.Printf("User id: %d updated its encypted password successfully.", userId)
|
||||
log.Printf("User id: %d updated its encypted password successfully.", userID)
|
||||
} else {
|
||||
log.Printf("User id: %d already has its encrypted password.", userId)
|
||||
log.Printf("User id: %d already has its encrypted password.", userID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -68,6 +68,6 @@ func main() {
|
||||
|
||||
beego.BConfig.WebConfig.Session.SessionOn = true
|
||||
dao.InitDB()
|
||||
updateInitPassword(ADMIN_USER_ID, os.Getenv("HARBOR_ADMIN_PASSWORD"))
|
||||
updateInitPassword(adminUserID, os.Getenv("HARBOR_ADMIN_PASSWORD"))
|
||||
beego.Run()
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -1,20 +0,0 @@
|
||||
/*
|
||||
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 AuthModel struct {
|
||||
Principal string
|
||||
Password string
|
||||
}
|
@ -12,4 +12,11 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package test
|
||||
|
||||
package models
|
||||
|
||||
// AuthModel holds information used to authenticate.
|
||||
type AuthModel struct {
|
||||
Principal string
|
||||
Password string
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
/*
|
||||
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 ProjectRole struct {
|
||||
PrId int
|
||||
ProjectId int64
|
||||
RoleId int
|
||||
}
|
@ -1,25 +1,45 @@
|
||||
/*
|
||||
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
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Repo holds information about repositories.
|
||||
type Repo struct {
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
|
||||
type Repo struct {
|
||||
Repositories []string `json:repositories`
|
||||
// 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"`
|
||||
DockerVersion string `json:"Docker Version"`
|
||||
Os string `json:"OS"`
|
||||
//Size int `json:"Size"`
|
||||
}
|
||||
|
||||
// Tag holds information about a tag.
|
||||
type Tag struct {
|
||||
Version string `json:"version"`
|
||||
ImageID string `json:"image_id"`
|
||||
}
|
||||
|
@ -1,32 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
)
|
||||
|
||||
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"`
|
||||
//Size int `json:"Size"`
|
||||
}
|
@ -1,28 +1,48 @@
|
||||
/*
|
||||
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)"`
|
||||
}
|
||||
|
||||
// ProjectRole holds information about the relationship of project and role.
|
||||
type ProjectRole struct {
|
||||
PrID int `orm:"column(pr_id)" json:"PrId"`
|
||||
ProjectID int64 `orm:"column(project_id)" json:"ProjectId"`
|
||||
RoleID int `orm:"column(role_id)" json:"RoleId"`
|
||||
}
|
||||
|
||||
// UserProjectRole holds information about relationship of user, project and role.
|
||||
type UserProjectRole struct {
|
||||
UprID int `orm:"column(upr_id)" json:"UprId"`
|
||||
UserID int `orm:"column(user_id)" json:"UserId"`
|
||||
PrID int64 `orm:"column(pr_id)" json:"PrId"`
|
||||
}
|
||||
|
@ -1,20 +0,0 @@
|
||||
/*
|
||||
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 Tag struct {
|
||||
Version string `json:version`
|
||||
ImageId string `json:image_id`
|
||||
}
|
@ -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)"`
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
/*
|
||||
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 UserProjectRole struct {
|
||||
UprId int
|
||||
UserId int
|
||||
PrId int64
|
||||
}
|
@ -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{})
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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 {
|
@ -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
|
||||
}
|
||||
|
@ -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
|
@ -1,8 +1,8 @@
|
||||
page_title_index = Harbor
|
||||
page_title_sign_in = Sign In - Harbor
|
||||
page_title_project = Project - Harbor
|
||||
page_title_item_details = Item Details - Harbor
|
||||
page_title_registration = Registration - Harbor
|
||||
page_title_item_details = Details - Harbor
|
||||
page_title_registration = Sign Up - Harbor
|
||||
page_title_forgot_password = Forgot Password - Harbor
|
||||
title_forgot_password = Forgot Password
|
||||
page_title_reset_password = Reset Password - Harbor
|
||||
@ -13,7 +13,7 @@ page_title_search = Search - Harbor
|
||||
sign_in = Sign In
|
||||
sign_up = Sign Up
|
||||
log_out = Log Out
|
||||
search_placeholder = Search for projects or repositories
|
||||
search_placeholder = projects or repositories
|
||||
change_password = Change Password
|
||||
username_email = Username/Email
|
||||
password = Password
|
||||
@ -26,26 +26,26 @@ project_name = Project Name
|
||||
creation_time = Creation Time
|
||||
publicity = Publicity
|
||||
add_project = Add Project
|
||||
check_for_publicity = Check this checkbox and the project will be public.
|
||||
check_for_publicity = Public project
|
||||
button_save = Save
|
||||
button_close = Close
|
||||
button_cancel = Cancel
|
||||
button_submit = Submit
|
||||
username = Username
|
||||
email = Email
|
||||
system_admin = System Admin
|
||||
dlg_button_ok = OK
|
||||
dlg_button_cancel = Cancel
|
||||
registration = Registration
|
||||
registration = Sign Up
|
||||
username_description = This will be your username.
|
||||
email_description = You will occasionally receive account related emails. We promise not to share your email with anyone.
|
||||
email_description = The Email address will be used for resetting password.
|
||||
full_name = Full Name
|
||||
full_name_description = First name & Last name.
|
||||
password_description = Use more than seven characters with at least one lowercase letter, one capital letter and one numeral.
|
||||
password_description = At least 7 characters with 1 lowercase letter, 1 capital letter and 1 numeric character.
|
||||
confirm_password = Confirm Password
|
||||
note_to_the_admin = Notes to the Administrator
|
||||
note_to_the_admin = Comments
|
||||
old_password = Old Password
|
||||
new_password = New Password
|
||||
forgot_password_description = Please input the email for your account, and a link to reset password will be sent.
|
||||
forgot_password_description = Please input the Email used when you signed up, a reset password Email will be sent to you.
|
||||
|
||||
projects = Projects
|
||||
repositories = Repositories
|
||||
@ -58,27 +58,26 @@ user = Users
|
||||
logs = Logs
|
||||
repo_name = Repository Name
|
||||
add_members = Add Members
|
||||
role_info = Role Info
|
||||
operation = Operation
|
||||
advance = Advance
|
||||
advance = Advanced Search
|
||||
all = All
|
||||
others = Others
|
||||
start_date = Start Date
|
||||
end_date = End Date
|
||||
timestamp = Timestamp
|
||||
role = Role
|
||||
reset_email_hint = Please use this link to reset your password
|
||||
reset_email_hint = Please click this link to reset your password
|
||||
reset_email_subject = Reset your password
|
||||
language = English
|
||||
language_en-US = English
|
||||
language_zh-CN = 中文
|
||||
copyright = Copyright
|
||||
all_rights_reserved = All rights reserved.
|
||||
container_registry_management = Container Registry Management
|
||||
index_desc = Project Harbor is to build an enterprise-class, reliable registry server. Enterprises can set up a private registry server in their own environment to improve productivity as well as security. Project Harbor can be used in both development and production environment.
|
||||
index_desc_0 = Key benefits of Project Harbor:
|
||||
index_desc_0 = Key benefits:
|
||||
index_desc_1 = 1. Security: Keep their intellectual properties within their organizations.
|
||||
index_desc_2 = 2. Efficiency: A private registry server is set up within the organization's network and can reduce significantly the internet traffic to the public service.
|
||||
index_desc_3 = 3. Access Control: RBAC(Role Based Access Control) is provided. User management can be integrated with existing enterprise identity services like AD/LDAP.
|
||||
index_desc_3 = 3. Access Control: RBAC (Role Based Access Control) is provided. User management can be integrated with existing enterprise identity services like AD/LDAP.
|
||||
index_desc_4 = 4. Audit: All access to the registry are logged and can be used for audit purpose.
|
||||
index_desc_5 = 5. GUI: User friendly single-pane-of-glass management console.
|
||||
|
||||
|
@ -14,92 +14,92 @@
|
||||
*/
|
||||
var global_messages = {
|
||||
"username_is_required" : {
|
||||
"en-US": "Username is required!",
|
||||
"en-US": "Username is required.",
|
||||
"zh-CN": "用户名为必填项。"
|
||||
},
|
||||
"username_has_been_taken" : {
|
||||
"en-US": "Username has been taken!",
|
||||
"zh-CN": "用户名已被占用!"
|
||||
"en-US": "Username has been taken.",
|
||||
"zh-CN": "用户名已被占用。"
|
||||
},
|
||||
"username_is_too_long" : {
|
||||
"en-US": "Username is too long (maximum is 20 characters)",
|
||||
"zh-CN": "用户名内容长度超出字符数限制。(最长为20个字符)"
|
||||
"en-US": "Username is too long. (maximum 20 characters)",
|
||||
"zh-CN": "用户名长度超出限制。(最长为20个字符)"
|
||||
},
|
||||
"username_contains_illegal_chars": {
|
||||
"en-US": "Username contains illegal characters.",
|
||||
"en-US": "Username contains illegal character(s).",
|
||||
"zh-CN": "用户名包含不合法的字符。"
|
||||
},
|
||||
"email_is_required" : {
|
||||
"en-US": "Email is required!",
|
||||
"en-US": "Email is required.",
|
||||
"zh-CN": "邮箱为必填项。"
|
||||
},
|
||||
"email_contains_illegal_chars" : {
|
||||
"en-US": "Email contains illegal characters.",
|
||||
"en-US": "Email contains illegal character(s).",
|
||||
"zh-CN": "邮箱包含不合法的字符。"
|
||||
},
|
||||
"email_has_been_taken" : {
|
||||
"en-US": "Email has been taken!",
|
||||
"zh-CN": "邮箱已被占用!"
|
||||
"en-US": "Email has been taken.",
|
||||
"zh-CN": "邮箱已被占用。"
|
||||
},
|
||||
"email_content_illegal" : {
|
||||
"en-US": "Email content is illegal.",
|
||||
"zh-CN": "邮箱格式不合法!"
|
||||
"en-US": "Email format is illegal.",
|
||||
"zh-CN": "邮箱格式不合法。"
|
||||
},
|
||||
"email_does_not_exist" : {
|
||||
"en-US": "Email does not exist!",
|
||||
"en-US": "Email does not exist.",
|
||||
"zh-CN": "邮箱不存在。"
|
||||
},
|
||||
"realname_is_required" : {
|
||||
"en-US": "Realname is required!",
|
||||
"en-US": "Full name is required.",
|
||||
"zh-CN": "全名为必填项。"
|
||||
},
|
||||
"realname_is_too_long" : {
|
||||
"en-US": "Realname is too long (maximum is 20 characters)",
|
||||
"zh-CN": "全名内容长度超出字符数限制。(最长为20个字符)"
|
||||
"en-US": "Full name is too long. (maximum 20 characters)",
|
||||
"zh-CN": "全名长度超出限制。(最长为20个字符)"
|
||||
},
|
||||
"realname_contains_illegal_chars" : {
|
||||
"en-US": "Realname contains illegal characters.",
|
||||
"en-US": "Full name contains illegal character(s).",
|
||||
"zh-CN": "全名包含不合法的字符。"
|
||||
},
|
||||
"password_is_required" : {
|
||||
"en-US": "Password is required!",
|
||||
"en-US": "Password is required.",
|
||||
"zh-CN": "密码为必填项。"
|
||||
},
|
||||
"password_is_invalid" : {
|
||||
"en-US": "Password is invalid. Use more than seven characters with at least one lowercase letter, one capital letter and one numeral.",
|
||||
"zh-CN": "密码无效。至少输入 7个字符且包含 1个小写字母,1个大写字母和数字。"
|
||||
"en-US": "Password is invalid. At least 7 characters with 1 lowercase letter, 1 capital letter and 1 numeric character.",
|
||||
"zh-CN": "密码无效。至少输入 7个字符且包含 1个小写字母,1个大写字母和 1个数字。"
|
||||
},
|
||||
"password_is_too_long" : {
|
||||
"en-US": "Password is too long (maximum is 20 characters)",
|
||||
"zh-CN": "密码内容长度超出字符数限制。(最长为20个字符)"
|
||||
"en-US": "Password is too long. (maximum 20 characters)",
|
||||
"zh-CN": "密码长度超出限制。(最长为20个字符)"
|
||||
},
|
||||
"password_does_not_match" : {
|
||||
"en-US": "Password does not match the confirmation.",
|
||||
"zh-CN": "两次密码输入内容不一致。"
|
||||
"en-US": "Passwords do not match.",
|
||||
"zh-CN": "两次密码输入不一致。"
|
||||
},
|
||||
"comment_is_too_long" : {
|
||||
"en-US": "Comment is too long (maximum is 20 characters)",
|
||||
"zh-CN": "留言内容长度超过字符数限制。(最长为20个字符)"
|
||||
"en-US": "Comment is too long. (maximum 20 characters)",
|
||||
"zh-CN": "备注长度超出限制。(最长为20个字符)"
|
||||
},
|
||||
"comment_contains_illegal_chars" : {
|
||||
"en-US": "Comment contains illegal characters.",
|
||||
"zh-CN": "留言内容包含不合法的字符。"
|
||||
"en-US": "Comment contains illegal character(s).",
|
||||
"zh-CN": "备注包含不合法的字符。"
|
||||
},
|
||||
"project_name_is_required" : {
|
||||
"en-US": "Project name is required!",
|
||||
"en-US": "Project name is required.",
|
||||
"zh-CN": "项目名称为必填项。"
|
||||
},
|
||||
"project_name_is_too_short" : {
|
||||
"en-US": "Project name is too short (minimum is 4 characters)",
|
||||
"zh-CN": "项目名称内容过于简短。(最少要求4个字符)"
|
||||
"en-US": "Project name is too short. (minimum 4 characters)",
|
||||
"zh-CN": "项目名称至少要求 4个字符。"
|
||||
},
|
||||
"project_name_is_too_long" : {
|
||||
"en-US": "Project name is too long (maximum is 30 characters)",
|
||||
"zh-CN": "项目名称内容长度超出字符数限制。(最长为30个字符)"
|
||||
"en-US": "Project name is too long. (maximum 30 characters)",
|
||||
"zh-CN": "项目名称长度超出限制。(最长为30个字符)"
|
||||
},
|
||||
"project_name_contains_illegal_chars" : {
|
||||
"en-US": "Project name contains illegal characters.",
|
||||
"zh-CN": "项目名称内容包含不合法的字符。"
|
||||
"en-US": "Project name contains illegal character(s).",
|
||||
"zh-CN": "项目名称包含不合法的字符。"
|
||||
},
|
||||
"project_exists" : {
|
||||
"en-US": "Project exists.",
|
||||
@ -118,7 +118,7 @@ var global_messages = {
|
||||
"zh-CN": "请输入用户名和密码。"
|
||||
},
|
||||
"check_your_username_or_password" : {
|
||||
"en-US": "Please check your username or password!",
|
||||
"en-US": "Please check your username or password.",
|
||||
"zh-CN": "请输入正确的用户名或密码。"
|
||||
},
|
||||
"title_login_failed" : {
|
||||
@ -130,27 +130,27 @@ var global_messages = {
|
||||
"zh-CN": "修改密码"
|
||||
},
|
||||
"change_password_successfully" : {
|
||||
"en-US": "Changed password successfully.",
|
||||
"zh-CN": "修改密码操作成功。"
|
||||
"en-US": "Password changed successfully.",
|
||||
"zh-CN": "密码已修改。"
|
||||
},
|
||||
"title_forgot_password" : {
|
||||
"en-US": "Forgot Password",
|
||||
"zh-CN": "忘记密码"
|
||||
},
|
||||
"email_has_been_sent" : {
|
||||
"en-US": "Email has been sent.",
|
||||
"en-US": "Email for resetting password has been sent.",
|
||||
"zh-CN": "重置密码邮件已发送。"
|
||||
},
|
||||
"send_email_failed" : {
|
||||
"en-US": "Send email failed.",
|
||||
"zh-CN": "邮件发送失败。"
|
||||
"en-US": "Failed to send Email for resetting password.",
|
||||
"zh-CN": "重置密码邮件发送失败。"
|
||||
},
|
||||
"please_login_first" : {
|
||||
"en-US": "Please login first!",
|
||||
"en-US": "Please login first.",
|
||||
"zh-CN": "请先登录。"
|
||||
},
|
||||
"old_password_is_not_correct" : {
|
||||
"en-US": "Old password input is not correct.",
|
||||
"en-US": "Old password is not correct.",
|
||||
"zh-CN": "原密码输入不正确。"
|
||||
},
|
||||
"please_input_new_password" : {
|
||||
@ -158,15 +158,15 @@ var global_messages = {
|
||||
"zh-CN": "请输入新密码。"
|
||||
},
|
||||
"invalid_reset_url": {
|
||||
"en-US": "Invalid reset url",
|
||||
"zh-CN": "无效的重置链接"
|
||||
"en-US": "Invalid URL for resetting password.",
|
||||
"zh-CN": "无效密码重置链接。"
|
||||
},
|
||||
"reset_password_successfully" : {
|
||||
"en-US": "Reset password successfully.",
|
||||
"zh-CN": "密码重置成功。"
|
||||
},
|
||||
"internal_error": {
|
||||
"en-US": "Internal error, please contact sysadmin.",
|
||||
"en-US": "Internal error.",
|
||||
"zh-CN": "内部错误,请联系系统管理员。"
|
||||
},
|
||||
"title_reset_password" : {
|
||||
@ -178,11 +178,11 @@ var global_messages = {
|
||||
"zh-CN": "注册"
|
||||
},
|
||||
"registered_successfully": {
|
||||
"en-US": "Registered successfully.",
|
||||
"en-US": "Signed up successfully.",
|
||||
"zh-CN": "注册成功。"
|
||||
},
|
||||
"registered_failed" : {
|
||||
"en-US": "Registered failed.",
|
||||
"en-US": "Failed to sign up.",
|
||||
"zh-CN": "注册失败。"
|
||||
},
|
||||
"projects" : {
|
||||
@ -191,11 +191,11 @@ var global_messages = {
|
||||
},
|
||||
"repositories" : {
|
||||
"en-US": "Repositories",
|
||||
"zh-CN": "镜像资源"
|
||||
"zh-CN": "镜像仓库"
|
||||
},
|
||||
"no_repo_exists" :{
|
||||
"en-US": "No repositories exist.",
|
||||
"zh-CN": "没有镜像资源。"
|
||||
"en-US": "No repositories found, please use 'docker push' to upload images.",
|
||||
"zh-CN": "未发现镜像,请用‘docker push’命令上传镜像。"
|
||||
},
|
||||
"tag" : {
|
||||
"en-US": "Tag",
|
||||
@ -210,15 +210,15 @@ var global_messages = {
|
||||
"zh-CN": "镜像详细信息"
|
||||
},
|
||||
"add_members" : {
|
||||
"en-US": "Add Members",
|
||||
"en-US": "Add Member",
|
||||
"zh-CN": "添加成员"
|
||||
},
|
||||
"edit_members" : {
|
||||
"en-US": "Edit Members",
|
||||
"en-US": "Edit Member",
|
||||
"zh-CN": "编辑成员"
|
||||
},
|
||||
"add_member_failed" : {
|
||||
"en-US": "Add Member Failed",
|
||||
"en-US": "Adding Member Failed",
|
||||
"zh-CN": "添加成员失败"
|
||||
},
|
||||
"please_input_username" : {
|
||||
@ -230,29 +230,21 @@ var global_messages = {
|
||||
"zh-CN": "请为用户分配角色。"
|
||||
},
|
||||
"user_id_exists" : {
|
||||
"en-US": "User ID exists.",
|
||||
"zh-CN": "用户ID已存在。"
|
||||
"en-US": "User is already a member.",
|
||||
"zh-CN": "用户已经是成员。"
|
||||
},
|
||||
"user_id_does_not_exist" : {
|
||||
"en-US": "User ID does not exist.",
|
||||
"zh-CN": "不存在此用户ID。"
|
||||
"en-US": "User does not exist.",
|
||||
"zh-CN": "不存在此用户。"
|
||||
},
|
||||
"insuffient_authority" : {
|
||||
"en-US": "Insufficient authority.",
|
||||
"insufficient_privileges" : {
|
||||
"en-US": "Insufficient privileges.",
|
||||
"zh-CN": "权限不足。"
|
||||
},
|
||||
"operation_failed" : {
|
||||
"en-US": "Operation Failed",
|
||||
"zh-CN": "操作失败"
|
||||
},
|
||||
"network_error" : {
|
||||
"en-US": "Network Error",
|
||||
"zh-CN": "网络故障"
|
||||
},
|
||||
"network_error_description" : {
|
||||
"en-US": "Network error, please contact sysadmin.",
|
||||
"zh-CN": "网络故障, 请联系系统管理员。"
|
||||
},
|
||||
"button_on" : {
|
||||
"en-US": "On",
|
||||
"zh-CN": "打开"
|
||||
|
@ -13,7 +13,7 @@ page_title_search = 搜索 - Harbor
|
||||
sign_in = 登录
|
||||
sign_up = 注册
|
||||
log_out = 注销
|
||||
search_placeholder = 搜索项目或镜像资源
|
||||
search_placeholder = 项目或镜像名称
|
||||
change_password = 修改密码
|
||||
username_email = 用户名/邮箱
|
||||
password = 密码
|
||||
@ -25,10 +25,10 @@ admin_options = 管理员选项
|
||||
project_name = 项目名称
|
||||
creation_time = 创建时间
|
||||
publicity = 公开
|
||||
add_project = 新增项目
|
||||
check_for_publicity = 选中此项会使该项目对其他人公开。
|
||||
add_project = 新建项目
|
||||
check_for_publicity = 公开项目
|
||||
button_save = 保存
|
||||
button_close = 关闭
|
||||
button_cancel = 取消
|
||||
button_submit = 提交
|
||||
username = 用户名
|
||||
email = 邮箱
|
||||
@ -36,28 +36,28 @@ system_admin = 系统管理员
|
||||
dlg_button_ok = 确定
|
||||
dlg_button_cancel = 取消
|
||||
registration = 注册
|
||||
username_description = 在此填入登录时的用户名
|
||||
email_description = 您将定期通过邮件收到相关信息,我们保证不会将此邮箱告知他人。
|
||||
username_description = 在此填入登录时的用户名。
|
||||
email_description = 此邮箱将用于重置密码。
|
||||
full_name = 全名
|
||||
full_name_description = 姓氏和名称
|
||||
password_description = 至少输入7个字符且包含1个小写字母,1个大写字母和数字。
|
||||
full_name_description = 请输入全名。
|
||||
password_description = 至少输入 7个字符且包含 1个小写字母, 1个大写字母和 1个数字。
|
||||
confirm_password = 确认密码
|
||||
note_to_the_admin = 留言
|
||||
note_to_the_admin = 备注
|
||||
old_password = 原密码
|
||||
new_password = 新密码
|
||||
forgot_password_description = 输入您注册时使用的邮箱,我们将发送重置邮件链接到此邮箱。
|
||||
forgot_password_description = 重置邮件将发送到此邮箱。
|
||||
|
||||
projects = 项目
|
||||
repositories = 镜像资源
|
||||
repositories = 镜像仓库
|
||||
search = 搜索
|
||||
home = 首页
|
||||
project = 项目
|
||||
owner = 所有者
|
||||
repo = 镜像
|
||||
repo = 镜像仓库
|
||||
user = 用户
|
||||
logs = 日志
|
||||
repo_name = 镜像名称
|
||||
add_members = 添加成员
|
||||
role_info = 角色信息
|
||||
operation = 操作
|
||||
advance = 高级检索
|
||||
all = 全部
|
||||
@ -73,10 +73,10 @@ language_en-US = English
|
||||
language_zh-CN = 中文
|
||||
copyright = 版权所有
|
||||
all_rights_reserved = 保留所有权利。
|
||||
container_registry_management = 容器注册管理
|
||||
index_desc = Harbor是用于构建企业级、可信赖的容器注册管理系统。企业用户通过搭建Harbor,创建私有容器注册服务,提高生产力和安全管控。Harbor可同时应用于生产或开发环境。
|
||||
index_desc_0 = 使用Harbor的关键优势在于:
|
||||
index_desc_1 = 1. 安全: 确保知识产权在自己组织的管控之下。
|
||||
index_desc_2 = 2. 效率: 搭建于组织内部网络的私有容器注册系统可明显降低访问公共容器注册服务的带宽损耗。
|
||||
index_desc = Harbor是可靠的企业级Registry服务器。企业用户可使用Harbor搭建私有容器Registry服务,提高生产效率和安全度,既可应用于生产环境,也可以在开发环境中使用。
|
||||
index_desc_0 = 主要优点:
|
||||
index_desc_1 = 1. 安全: 确保知识产权在自己组织内部的管控之下。
|
||||
index_desc_2 = 2. 效率: 搭建组织内部的私有容器Registry服务,可显著降低访问公共Registry服务的网络需求。
|
||||
index_desc_3 = 3. 访问控制: 提供基于角色的访问控制,可集成企业目前拥有的用户管理系统(如:AD/LDAP)。
|
||||
index_desc_4 = 4. 审计: 所有访问注册服务的行为均被记录且可被用于日后审计。
|
||||
index_desc_4 = 4. 审计: 所有访问Registry服务的操作均被记录,便于日后审计。
|
||||
index_desc_5 = 5. 管理界面: 具有友好易用图形管理界面。
|
||||
|
@ -218,14 +218,14 @@ jQuery(function(){
|
||||
type: ajaxOpts.type,
|
||||
complete: function(jqXhr, status){
|
||||
if(jqXhr && jqXhr.status == 200){
|
||||
$("#btnClose").trigger("click");
|
||||
$("#btnCancel").trigger("click");
|
||||
listUser(null);
|
||||
}
|
||||
},
|
||||
errors: {
|
||||
404: i18n.getMessage("user_id_does_not_exist"),
|
||||
409: i18n.getMessage("user_id_exists"),
|
||||
403: i18n.getMessage("insuffient_authority")
|
||||
403: i18n.getMessage("insufficient_privileges")
|
||||
}
|
||||
}).exec();
|
||||
});
|
||||
@ -419,6 +419,24 @@ jQuery(function(){
|
||||
$("#spnFilterOption input[name=chkOperation]").prop("checked", $(this).prop("checked"));
|
||||
});
|
||||
|
||||
$("#spnFilterOption input[name=chkOperation]").on("click", function(){
|
||||
if(!$(this).prop("checked")){
|
||||
$("#spnFilterOption input[name=chkAll]").prop("checked", false);
|
||||
}
|
||||
|
||||
var selectedAll = true;
|
||||
|
||||
$("#spnFilterOption input[name=chkOperation]").each(function(i, e){
|
||||
if(!$(e).prop("checked")){
|
||||
selectedAll = false;
|
||||
}
|
||||
});
|
||||
|
||||
if(selectedAll){
|
||||
$("#spnFilterOption input[name=chkAll]").prop("checked", true);
|
||||
}
|
||||
});
|
||||
|
||||
function getKeyWords(){
|
||||
var keywords = "";
|
||||
var checkedItemList=$("#spnFilterOption input[name=chkOperation]:checked");
|
||||
|
@ -132,7 +132,7 @@ jQuery(function(){
|
||||
409: i18n.getMessage("project_exists")
|
||||
},
|
||||
complete: function(jqXhr, status){
|
||||
$("#btnClose").trigger("click");
|
||||
$("#btnCancel").trigger("click");
|
||||
listProject(null, currentPublic);
|
||||
}
|
||||
}).exec();
|
||||
|
@ -1 +0,0 @@
|
||||
##dummy##
|
@ -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))
|
||||
}
|
||||
|
@ -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 == "" {
|
||||
|
@ -1,145 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
|
||||
const SESSION_COOKIE = "beegosessionID"
|
||||
|
||||
func BuildRegistryUrl(segments ...string) string {
|
||||
registryURL := os.Getenv("REGISTRY_URL")
|
||||
if registryURL == "" {
|
||||
registryURL = "http://localhost:5000"
|
||||
}
|
||||
url := registryURL + "/v2"
|
||||
for _, s := range segments {
|
||||
if s == "v2" {
|
||||
beego.Error("Unnecessary v2 in", segments)
|
||||
continue
|
||||
}
|
||||
url += "/" + s
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
func HttpGet(url, sessionId, username, password string) ([]byte, error) {
|
||||
response, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode == http.StatusOK {
|
||||
return result, nil
|
||||
} else if response.StatusCode == http.StatusUnauthorized {
|
||||
authenticate := response.Header.Get("WWW-Authenticate")
|
||||
str := strings.Split(authenticate, " ")[1]
|
||||
beego.Trace("url: " + url)
|
||||
beego.Trace("Authentication Header: " + str)
|
||||
var realm string
|
||||
var service string
|
||||
var scope string
|
||||
strs := strings.Split(str, ",")
|
||||
for _, s := range strs {
|
||||
if strings.Contains(s, "realm") {
|
||||
realm = s
|
||||
} else if strings.Contains(s, "service") {
|
||||
service = s
|
||||
} else if strings.Contains(s, "scope") {
|
||||
strings.HasSuffix(url, "v2/_catalog")
|
||||
scope = s
|
||||
}
|
||||
}
|
||||
realm = strings.Split(realm, "\"")[1]
|
||||
service = strings.Split(service, "\"")[1]
|
||||
scope = strings.Split(scope, "\"")[1]
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(sessionId) > 0 {
|
||||
cookie := &http.Cookie{Name: SESSION_COOKIE, Value: sessionId, Path: "/"}
|
||||
request.AddCookie(cookie)
|
||||
} else {
|
||||
request.SetBasicAuth(username, password)
|
||||
}
|
||||
response, err = client.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err = ioutil.ReadAll(response.Body)
|
||||
|
||||
defer response.Body.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if response.StatusCode == http.StatusOK {
|
||||
tt := make(map[string]string)
|
||||
json.Unmarshal(result, &tt)
|
||||
request, err = http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Header.Add("Authorization", "Bearer "+tt["token"])
|
||||
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
if len(via) >= 10 {
|
||||
return fmt.Errorf("too many redirects")
|
||||
}
|
||||
for k, v := range via[0].Header {
|
||||
if _, ok := req.Header[k]; !ok {
|
||||
req.Header[k] = v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
response, err = client.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err = ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return result, nil
|
||||
} else {
|
||||
return nil, errors.New(string(result))
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New(string(result))
|
||||
}
|
||||
}
|
@ -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]
|
||||
}
|
||||
|
@ -31,6 +31,7 @@
|
||||
<p>{{i18n .Lang "index_desc_2"}}</p>
|
||||
<p>{{i18n .Lang "index_desc_3"}}</p>
|
||||
<p>{{i18n .Lang "index_desc_4"}}</p>
|
||||
<p>{{i18n .Lang "index_desc_5"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- /container -->
|
||||
|
@ -82,7 +82,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n .Lang "username"}}</th>
|
||||
<th>{{i18n .Lang "role_info"}}</th>
|
||||
<th>{{i18n .Lang "role"}}</th>
|
||||
<th>{{i18n .Lang "operation"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -214,7 +214,7 @@
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" id="btnSave">{{i18n .Lang "button_save"}}</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal" id="btnClose">{{i18n .Lang "button_close"}}</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal" id="btnCancel">{{i18n .Lang "button_cancel"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -83,7 +83,7 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<a type="button" class="close" data-dismiss="modal" aria-label="Close" id="btnClose">
|
||||
<a type="button" class="close" data-dismiss="modal" aria-label="Close" id="btnCancel">
|
||||
<span aria-hidden="true">×</span>
|
||||
</a>
|
||||
<h4 class="modal-title" id="dlgAddProjectTitle">{{i18n .Lang "add_project"}}</h4>
|
||||
@ -105,7 +105,7 @@
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" id="btnSave">{{i18n .Lang "button_save"}}</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{i18n .Lang "button_close"}}</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{i18n .Lang "button_cancel"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user