mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 02:35:17 +01:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
9fe1ef41b6
@ -1,62 +1,67 @@
|
||||
log:
|
||||
build: ./log/
|
||||
volumes:
|
||||
- /var/log/harbor/:/var/log/docker/
|
||||
ports:
|
||||
- 1514:514
|
||||
registry:
|
||||
image: library/registry:2.3.0
|
||||
volumes:
|
||||
- /data/registry:/storage
|
||||
- ./config/registry/:/etc/registry/
|
||||
ports:
|
||||
- 5001:5001
|
||||
command:
|
||||
/etc/registry/config.yml
|
||||
links:
|
||||
- log
|
||||
log_driver: "syslog"
|
||||
log_opt:
|
||||
syslog-address: "tcp://127.0.0.1:1514"
|
||||
syslog-tag: "registry"
|
||||
mysql:
|
||||
build: ./db/
|
||||
volumes:
|
||||
- /data/database:/var/lib/mysql
|
||||
env_file:
|
||||
- ./config/db/env
|
||||
links:
|
||||
- log
|
||||
log_driver: "syslog"
|
||||
log_opt:
|
||||
syslog-address: "tcp://127.0.0.1:1514"
|
||||
syslog-tag: "mysql"
|
||||
ui:
|
||||
build: ../
|
||||
env_file:
|
||||
- ./config/ui/env
|
||||
volumes:
|
||||
- ./config/ui/app.conf:/etc/ui/app.conf
|
||||
- ./config/ui/private_key.pem:/etc/ui/private_key.pem
|
||||
links:
|
||||
- registry
|
||||
- mysql
|
||||
- log
|
||||
log_driver: "syslog"
|
||||
log_opt:
|
||||
syslog-address: "tcp://127.0.0.1:1514"
|
||||
syslog-tag: "ui"
|
||||
proxy:
|
||||
image: library/nginx:1.9
|
||||
volumes:
|
||||
- ./config/nginx:/etc/nginx
|
||||
links:
|
||||
- ui
|
||||
- registry
|
||||
- log
|
||||
ports:
|
||||
- 80:80
|
||||
log_driver: "syslog"
|
||||
log_opt:
|
||||
syslog-address: "tcp://127.0.0.1:1514"
|
||||
syslog-tag: "proxy"
|
||||
version: '2'
|
||||
services:
|
||||
log:
|
||||
build: ./log/
|
||||
volumes:
|
||||
- /var/log/harbor/:/var/log/docker/
|
||||
ports:
|
||||
- 1514:514
|
||||
registry:
|
||||
image: library/registry:2.3.0
|
||||
volumes:
|
||||
- /data/registry:/storage
|
||||
- ./config/registry/:/etc/registry/
|
||||
ports:
|
||||
- 5001:5001
|
||||
command:
|
||||
/etc/registry/config.yml
|
||||
depends_on:
|
||||
- log
|
||||
logging:
|
||||
driver: "syslog"
|
||||
options:
|
||||
syslog-address: "tcp://127.0.0.1:1514"
|
||||
syslog-tag: "registry"
|
||||
mysql:
|
||||
build: ./db/
|
||||
volumes:
|
||||
- /data/database:/var/lib/mysql
|
||||
env_file:
|
||||
- ./config/db/env
|
||||
depends_on:
|
||||
- log
|
||||
logging:
|
||||
driver: "syslog"
|
||||
options:
|
||||
syslog-address: "tcp://127.0.0.1:1514"
|
||||
syslog-tag: "mysql"
|
||||
ui:
|
||||
build: ../
|
||||
env_file:
|
||||
- ./config/ui/env
|
||||
volumes:
|
||||
- ./config/ui/app.conf:/etc/ui/app.conf
|
||||
- ./config/ui/private_key.pem:/etc/ui/private_key.pem
|
||||
depends_on:
|
||||
- log
|
||||
logging:
|
||||
driver: "syslog"
|
||||
options:
|
||||
syslog-address: "tcp://127.0.0.1:1514"
|
||||
syslog-tag: "ui"
|
||||
proxy:
|
||||
image: library/nginx:1.9
|
||||
volumes:
|
||||
- ./config/nginx:/etc/nginx
|
||||
ports:
|
||||
- 80:80
|
||||
depends_on:
|
||||
- mysql
|
||||
- registry
|
||||
- ui
|
||||
- log
|
||||
logging:
|
||||
driver: "syslog"
|
||||
options:
|
||||
syslog-address: "tcp://127.0.0.1:1514"
|
||||
syslog-tag: "proxy"
|
||||
|
@ -1,24 +1,35 @@
|
||||
## CONFIGURATIONS
|
||||
#The endpoint for user to access UI and registry service
|
||||
hostname = mydomain.com
|
||||
#The protocol for accessing the UI and token/notification service, by default it is http
|
||||
#User can set it to https if ssl is setup on nginx
|
||||
## Configuration file of Harbor
|
||||
|
||||
#The IP address or hostname to access admin UI and registry service.
|
||||
#DO NOT use localhost or 127.0.0.1, because Harbor needs to be accessed by external clients.
|
||||
hostname = reg.mydomain.com
|
||||
|
||||
#The protocol for accessing the UI and token/notification service, by default it is http.
|
||||
#It can be set to https if ssl is enabled on nginx.
|
||||
ui_url_protocol = http
|
||||
#Email settings for ui to send password resetting emails
|
||||
|
||||
#Email account settings for sending out password resetting emails.
|
||||
email_server = smtp.mydomain.com
|
||||
email_server_port = 25
|
||||
email_username = sample_admin@mydomain.com
|
||||
email_password = abc
|
||||
email_from = admin <sample_admin@mydomain.com>
|
||||
##The password of harbor admin
|
||||
|
||||
##The password of Harbor admin, change this before any production use.
|
||||
harbor_admin_password= Harbor12345
|
||||
##By default the auth mode is db_auth, i.e. the creadentials are stored in a databse
|
||||
#please set it to ldap_auth if you want to verify user's credentials against an ldap server.
|
||||
|
||||
##By default the auth mode is db_auth, i.e. the credentials are stored in a local database.
|
||||
#Set it to ldap_auth if you want to verify a user's credentials against an LDAP server.
|
||||
auth_mode = db_auth
|
||||
#The url for ldap endpoint
|
||||
|
||||
#The url for an ldap endpoint.
|
||||
ldap_url = ldaps://ldap.mydomain.com
|
||||
#The basedn template for verifying the user's password
|
||||
|
||||
#The basedn template to look up a user in LDAP and verify the user's password.
|
||||
ldap_basedn = uid=%s,ou=people,dc=mydomain,dc=com
|
||||
#The password for root user of db
|
||||
|
||||
#The password for the root user of mysql db, change this before any production use.
|
||||
db_password = root123
|
||||
#Switch for self-registration feature
|
||||
self_registration = on
|
||||
#####
|
||||
|
@ -19,7 +19,7 @@ http:
|
||||
auth:
|
||||
token:
|
||||
issuer: registry-token-issuer
|
||||
realm: http://localhost/service/token
|
||||
realm: http://registry/service/token
|
||||
rootcertbundle: /etc/registry/root.crt
|
||||
service: token-service
|
||||
|
||||
@ -27,7 +27,7 @@ notifications:
|
||||
endpoints:
|
||||
- name: harbor
|
||||
disabled: false
|
||||
url: http://localhost/service/notifications
|
||||
url: http://registry/service/notifications
|
||||
timeout: 500
|
||||
threshold: 5
|
||||
backoff: 1000
|
@ -7,9 +7,9 @@ metadata:
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- name: bbb
|
||||
- name: http
|
||||
port: 80
|
||||
- name: aaa
|
||||
- name: https
|
||||
port: 443
|
||||
selector:
|
||||
name: proxy
|
@ -20,6 +20,7 @@ spec:
|
||||
ports:
|
||||
- containerPort: 5000
|
||||
- containerPort: 5001
|
||||
- containerPort: 53
|
||||
volumeMounts:
|
||||
- name: storage
|
||||
mountPath: /storage
|
@ -11,5 +11,7 @@ spec:
|
||||
port: 5000
|
||||
- name: external
|
||||
port: 5001
|
||||
- name: aa
|
||||
port: 53
|
||||
selector:
|
||||
name: registry
|
@ -25,6 +25,7 @@ auth_mode = cp.get("configuration", "auth_mode")
|
||||
ldap_url = cp.get("configuration", "ldap_url")
|
||||
ldap_basedn = cp.get("configuration", "ldap_basedn")
|
||||
db_password = cp.get("configuration", "db_password")
|
||||
self_registration = cp.get("configuration", "self_registration")
|
||||
########
|
||||
|
||||
base_dir = os.path.dirname(__file__)
|
||||
@ -60,11 +61,13 @@ for f in conf_files:
|
||||
render(os.path.join(templates_dir, "ui", "env"),
|
||||
ui_conf_env,
|
||||
hostname=hostname,
|
||||
db_password=db_password,
|
||||
ui_url=ui_url,
|
||||
auth_mode=auth_mode,
|
||||
admin_pwd=harbor_admin_password,
|
||||
ldap_url=ldap_url,
|
||||
ldap_basedn=ldap_basedn)
|
||||
ldap_basedn=ldap_basedn,
|
||||
self_registration=self_registration)
|
||||
|
||||
render(os.path.join(templates_dir, "ui", "app.conf"),
|
||||
ui_conf,
|
||||
|
@ -27,7 +27,7 @@ notifications:
|
||||
endpoints:
|
||||
- name: harbor
|
||||
disabled: false
|
||||
url: $ui_url/service/notifications
|
||||
url: http://ui/service/notifications
|
||||
timeout: 500
|
||||
threshold: 5
|
||||
backoff: 1000
|
||||
|
@ -1,5 +1,7 @@
|
||||
MYSQL_HOST=mysql
|
||||
MYSQL_PORT=3306
|
||||
MYSQL_USR=root
|
||||
MYSQL_PWD=$db_password
|
||||
REGISTRY_URL=http://registry:5000
|
||||
CONFIG_PATH=/etc/ui/app.conf
|
||||
HARBOR_REG_URL=$hostname
|
||||
@ -8,4 +10,5 @@ HARBOR_URL=$ui_url
|
||||
AUTH_MODE=$auth_mode
|
||||
LDAP_URL=$ldap_url
|
||||
LDAP_BASE_DN=$ldap_basedn
|
||||
SELF_REGISTRATION=$self_registration
|
||||
LOG_LEVEL=debug
|
||||
|
@ -16,12 +16,14 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/beego/i18n"
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
// CommonController handles request from UI that doesn't expect a page, such as /login /logout ...
|
||||
@ -38,6 +40,9 @@ func (c *CommonController) Render() error {
|
||||
type BaseController struct {
|
||||
beego.Controller
|
||||
i18n.Locale
|
||||
SelfRegistration bool
|
||||
IsAdmin bool
|
||||
AuthMode string
|
||||
}
|
||||
|
||||
type langType struct {
|
||||
@ -93,15 +98,35 @@ func (b *BaseController) Prepare() {
|
||||
b.Data["CurLang"] = curLang.Name
|
||||
b.Data["RestLangs"] = restLangs
|
||||
|
||||
sessionUserID := b.GetSession("userId")
|
||||
if sessionUserID != nil {
|
||||
b.Data["Username"] = b.GetSession("username")
|
||||
}
|
||||
authMode := os.Getenv("AUTH_MODE")
|
||||
authMode := strings.ToLower(os.Getenv("AUTH_MODE"))
|
||||
if authMode == "" {
|
||||
authMode = "db_auth"
|
||||
}
|
||||
b.Data["AuthMode"] = authMode
|
||||
b.AuthMode = authMode
|
||||
b.Data["AuthMode"] = b.AuthMode
|
||||
|
||||
selfRegistration := strings.ToLower(os.Getenv("SELF_REGISTRATION"))
|
||||
|
||||
if selfRegistration == "on" {
|
||||
b.SelfRegistration = true
|
||||
}
|
||||
|
||||
sessionUserID := b.GetSession("userId")
|
||||
if sessionUserID != nil {
|
||||
b.Data["Username"] = b.GetSession("username")
|
||||
b.Data["UserId"] = sessionUserID.(int)
|
||||
|
||||
var err error
|
||||
b.IsAdmin, err = dao.IsAdminRole(sessionUserID.(int))
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in IsAdminRole:%v", err)
|
||||
b.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
}
|
||||
|
||||
b.Data["IsAdmin"] = b.IsAdmin
|
||||
b.Data["SelfRegistration"] = b.SelfRegistration
|
||||
|
||||
}
|
||||
|
||||
// ForwardTo setup layout and template for content for a page.
|
||||
|
@ -17,11 +17,11 @@ package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
|
||||
"github.com/vmware/harbor/utils/log"
|
||||
)
|
||||
|
||||
@ -32,35 +32,70 @@ type RegisterController struct {
|
||||
|
||||
// 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" {
|
||||
|
||||
if !rc.SelfRegistration {
|
||||
log.Warning("Registration is disabled when self-registration is off.")
|
||||
rc.Redirect("/signIn", http.StatusFound)
|
||||
}
|
||||
|
||||
if rc.AuthMode == "db_auth" {
|
||||
rc.ForwardTo("page_title_registration", "register")
|
||||
} else {
|
||||
rc.Redirect("/signIn", http.StatusNotFound)
|
||||
rc.Redirect("/signIn", http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
// AddUserController handles request for adding user with an admin role user
|
||||
type AddUserController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders the Sign In page, it only works if the auth mode is set to db_auth
|
||||
func (ac *AddUserController) Get() {
|
||||
|
||||
if !ac.IsAdmin {
|
||||
log.Warning("Add user can only be used by admin role user.")
|
||||
ac.Redirect("/signIn", http.StatusFound)
|
||||
}
|
||||
|
||||
if ac.AuthMode == "db_auth" {
|
||||
ac.ForwardTo("page_title_add_user", "register")
|
||||
} else {
|
||||
ac.Redirect("/signIn", http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
// 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"))
|
||||
realname := strings.TrimSpace(rc.GetString("realname"))
|
||||
password := strings.TrimSpace(rc.GetString("password"))
|
||||
comment := strings.TrimSpace(rc.GetString("comment"))
|
||||
func (cc *CommonController) SignUp() {
|
||||
|
||||
if !(cc.AuthMode == "db_auth") {
|
||||
cc.CustomAbort(http.StatusForbidden, "")
|
||||
}
|
||||
|
||||
if !(cc.SelfRegistration || cc.IsAdmin) {
|
||||
log.Warning("Registration can only be used by admin role user when self-registration is off.")
|
||||
cc.CustomAbort(http.StatusForbidden, "")
|
||||
}
|
||||
|
||||
username := strings.TrimSpace(cc.GetString("username"))
|
||||
email := strings.TrimSpace(cc.GetString("email"))
|
||||
realname := strings.TrimSpace(cc.GetString("realname"))
|
||||
password := strings.TrimSpace(cc.GetString("password"))
|
||||
comment := strings.TrimSpace(cc.GetString("comment"))
|
||||
|
||||
user := models.User{Username: username, Email: email, Realname: realname, Password: password, Comment: comment}
|
||||
|
||||
_, err := dao.Register(user)
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in Register: %v", err)
|
||||
rc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
}
|
||||
|
||||
// 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")
|
||||
func (cc *CommonController) UserExists() {
|
||||
target := cc.GetString("target")
|
||||
value := cc.GetString("value")
|
||||
|
||||
user := models.User{}
|
||||
switch target {
|
||||
@ -73,8 +108,8 @@ func (rc *CommonController) UserExists() {
|
||||
exist, err := dao.UserExists(user, target)
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in UserExists: %v", err)
|
||||
rc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
rc.Data["json"] = exist
|
||||
rc.ServeJSON()
|
||||
cc.Data["json"] = exist
|
||||
cc.ServeJSON()
|
||||
}
|
||||
|
13
dao/base.go
13
dao/base.go
@ -66,18 +66,11 @@ func GenerateRandomString() (string, error) {
|
||||
func InitDB() {
|
||||
orm.RegisterDriver("mysql", orm.DRMySQL)
|
||||
addr := os.Getenv("MYSQL_HOST")
|
||||
if len(addr) == 0 {
|
||||
addr = os.Getenv("MYSQL_PORT_3306_TCP_ADDR")
|
||||
}
|
||||
|
||||
port := os.Getenv("MYSQL_PORT_3306_TCP_PORT")
|
||||
port := os.Getenv("MYSQL_PORT")
|
||||
username := os.Getenv("MYSQL_USR")
|
||||
password := os.Getenv("MYSQL_PWD")
|
||||
|
||||
password := os.Getenv("MYSQL_ENV_MYSQL_ROOT_PASSWORD")
|
||||
if len(password) == 0 {
|
||||
password = os.Getenv("MYSQL_PWD")
|
||||
}
|
||||
|
||||
log.Debugf("db url: %s:%s, db user: %s", addr, port, username)
|
||||
dbStr := username + ":" + password + "@tcp(" + addr + ":" + port + ")/registry"
|
||||
ch := make(chan int, 1)
|
||||
go func() {
|
||||
|
@ -128,8 +128,8 @@ func TestMain(m *testing.M) {
|
||||
|
||||
log.Infof("DB_HOST: %s, DB_USR: %s, DB_PORT: %s, DB_PWD: %s\n", dbHost, dbUser, dbPort, dbPassword)
|
||||
|
||||
os.Setenv("MYSQL_PORT_3306_TCP_ADDR", dbHost)
|
||||
os.Setenv("MYSQL_PORT_3306_TCP_PORT", dbPort)
|
||||
os.Setenv("MYSQL_HOST", dbHost)
|
||||
os.Setenv("MYSQL_PORT", dbPort)
|
||||
os.Setenv("MYSQL_USR", dbUser)
|
||||
os.Setenv("MYSQL_PWD", dbPassword)
|
||||
os.Setenv("AUTH_MODE", "db_auth")
|
||||
|
@ -5,7 +5,7 @@ Harbor can be installed from the source code by using "docker-compose up" comman
|
||||
Harbor is deployed as several Docker containers. Hence, it can be deployed on any Linux distribution that supports Docker.
|
||||
Before deploying Harbor, the target machine requires Python, Docker, Docker Compose to be installed.
|
||||
* Python should be version 2.7 or higher. Some Linux distributions (Gentoo, Arch) may not have a Python interpreter installed by default. On those systems, you need to install Python manually.
|
||||
* The Docker engine should be version 1.8 or higher. For the details to install Docker engine, please refer to: https://docs.docker.com/engine/installation/
|
||||
* The Docker engine should be version 1.10 or higher. For the details to install Docker engine, please refer to: https://docs.docker.com/engine/installation/
|
||||
* The Docker Compose needs to be version 1.6.0 or higher. For the details to install Docker compose, please refer to: https://docs.docker.com/compose/install/
|
||||
|
||||
### Configuration of Harbor
|
||||
@ -139,4 +139,4 @@ Removing harbor_mysql_1 ... done
|
||||
|
||||
### Persistent data and log files
|
||||
By default, the data of database and image files in the registry are persisted in the directory **/data/** of the target machine. When Harbor's containers are removed and recreated, the data remain unchanged.
|
||||
Harbor leverages rsyslog to collect the logs of each container, by default the log files are stored in the directory **/var/log/harbor/** on Harbor's host.
|
||||
Harbor leverages rsyslog to collect the logs of each container, by default the log files are stored in the directory **/var/log/harbor/** on Harbor's host.
|
||||
|
@ -41,6 +41,7 @@ func init() {
|
||||
beego.Router("/", &controllers.IndexController{})
|
||||
beego.Router("/signIn", &controllers.SignInController{})
|
||||
beego.Router("/register", &controllers.RegisterController{})
|
||||
beego.Router("/addUser", &controllers.AddUserController{})
|
||||
beego.Router("/forgotPassword", &controllers.ForgotPasswordController{})
|
||||
beego.Router("/resetPassword", &controllers.ResetPasswordController{})
|
||||
beego.Router("/changePassword", &controllers.ChangePasswordController{})
|
||||
|
@ -42,13 +42,14 @@ func (a *TokenHandler) Get() {
|
||||
username, password, _ := request.BasicAuth()
|
||||
authenticated := authenticate(username, password)
|
||||
service := a.GetString("service")
|
||||
scope := a.GetString("scope")
|
||||
scopes := a.GetStrings("scope")
|
||||
log.Debugf("scopes: %+v", scopes)
|
||||
|
||||
if len(scope) == 0 && !authenticated {
|
||||
if len(scopes) == 0 && !authenticated {
|
||||
log.Info("login request with invalid credentials")
|
||||
a.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
access := svc_utils.GetResourceActions(scope)
|
||||
access := svc_utils.GetResourceActions(scopes)
|
||||
for _, a := range access {
|
||||
svc_utils.FilterAccess(username, authenticated, a)
|
||||
}
|
||||
|
@ -38,17 +38,19 @@ const (
|
||||
)
|
||||
|
||||
// GetResourceActions ...
|
||||
func GetResourceActions(scope string) []*token.ResourceActions {
|
||||
func GetResourceActions(scopes []string) []*token.ResourceActions {
|
||||
var res []*token.ResourceActions
|
||||
if scope == "" {
|
||||
return res
|
||||
for _, s := range scopes {
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
items := strings.Split(s, ":")
|
||||
res = append(res, &token.ResourceActions{
|
||||
Type: items[0],
|
||||
Name: items[1],
|
||||
Actions: strings.Split(items[2], ","),
|
||||
})
|
||||
}
|
||||
items := strings.Split(scope, ":")
|
||||
res = append(res, &token.ResourceActions{
|
||||
Type: items[0],
|
||||
Name: items[1],
|
||||
Actions: strings.Split(items[2], ","),
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
@ -66,9 +68,12 @@ func FilterAccess(username string, authenticated bool, a *token.ResourceActions)
|
||||
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" {
|
||||
isAdmin, err := dao.IsAdminRole(username)
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in IsAdminRole: %v", err)
|
||||
}
|
||||
if isAdmin {
|
||||
exist, err := dao.ProjectExists(projectName)
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in CheckExistProject: %v", err)
|
||||
@ -100,8 +105,8 @@ func FilterAccess(username string, authenticated bool, a *token.ResourceActions)
|
||||
}
|
||||
|
||||
// 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)
|
||||
func GenTokenForUI(username string, service string, scopes []string) (string, error) {
|
||||
access := GetResourceActions(scopes)
|
||||
for _, a := range access {
|
||||
FilterAccess(username, true, a)
|
||||
}
|
||||
|
@ -63,14 +63,15 @@ func RegistryAPIGet(url, username string) ([]byte, error) {
|
||||
authenticate := response.Header.Get("WWW-Authenticate")
|
||||
log.Debugf("authenticate header: %s", authenticate)
|
||||
var service string
|
||||
var scope string
|
||||
var scopes []string
|
||||
//Disregard the case for hanlding multiple scopes for http call initiated from UI, as there's refactor planned.
|
||||
re := regexp.MustCompile(`service=\"(.*?)\".*scope=\"(.*?)\"`)
|
||||
res := re.FindStringSubmatch(authenticate)
|
||||
if len(res) > 2 {
|
||||
service = res[1]
|
||||
scope = res[2]
|
||||
scopes = append(scopes, res[2])
|
||||
}
|
||||
token, err := GenTokenForUI(username, service, scope)
|
||||
token, err := GenTokenForUI(username, service, scopes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ page_title_sign_in = Sign In - Harbor
|
||||
page_title_project = Project - Harbor
|
||||
page_title_item_details = Details - Harbor
|
||||
page_title_registration = Sign Up - Harbor
|
||||
page_title_add_user = Add User - Harbor
|
||||
page_title_forgot_password = Forgot Password - Harbor
|
||||
title_forgot_password = Forgot Password
|
||||
page_title_reset_password = Reset Password - Harbor
|
||||
@ -12,6 +13,7 @@ title_change_password = Change Password
|
||||
page_title_search = Search - Harbor
|
||||
sign_in = Sign In
|
||||
sign_up = Sign Up
|
||||
add_user = Add User
|
||||
log_out = Log Out
|
||||
search_placeholder = projects or repositories
|
||||
change_password = Change Password
|
||||
|
@ -177,6 +177,10 @@ var global_messages = {
|
||||
"en-US": "Sign Up",
|
||||
"zh-CN": "注册"
|
||||
},
|
||||
"title_add_user": {
|
||||
"en-US": "Add User",
|
||||
"zh-CN": "新增用户"
|
||||
},
|
||||
"registered_successfully": {
|
||||
"en-US": "Signed up successfully.",
|
||||
"zh-CN": "注册成功。"
|
||||
@ -185,6 +189,14 @@ var global_messages = {
|
||||
"en-US": "Failed to sign up.",
|
||||
"zh-CN": "注册失败。"
|
||||
},
|
||||
"added_user_successfully": {
|
||||
"en-US": "Added user successfully.",
|
||||
"zh-CN": "新增用户成功。"
|
||||
},
|
||||
"added_user_failed": {
|
||||
"en-US": "Added user failed.",
|
||||
"zh-CN": "新增用户失败。"
|
||||
},
|
||||
"projects" : {
|
||||
"en-US": "Projects",
|
||||
"zh-CN": "项目"
|
||||
|
@ -3,6 +3,7 @@ page_title_sign_in = 登录 - Harbor
|
||||
page_title_project = 项目 - Harbor
|
||||
page_title_item_details = 详细信息 - Harbor
|
||||
page_title_registration = 注册 - Harbor
|
||||
page_title_add_user = 新增用户 - Harbor
|
||||
page_title_forgot_password = 忘记密码 - Harbor
|
||||
title_forgot_password = 忘记密码
|
||||
page_title_reset_password = 重置密码 - Harbor
|
||||
@ -12,6 +13,7 @@ title_change_password = 修改密码
|
||||
page_title_search = 搜索 - Harbor
|
||||
sign_in = 登录
|
||||
sign_up = 注册
|
||||
add_user = 新增用户
|
||||
log_out = 注销
|
||||
search_placeholder = 项目或镜像名称
|
||||
change_password = 修改密码
|
||||
|
@ -30,12 +30,14 @@ jQuery(function(){
|
||||
|
||||
$("#btnPageSignUp").on("click", function(){
|
||||
validateOptions.Validate(function() {
|
||||
var username = $.trim($("#Username").val());
|
||||
var email = $.trim($("#Email").val());
|
||||
var password = $.trim($("#Password").val());
|
||||
var confirmedPassword = $.trim($("#ConfirmedPassword").val());
|
||||
var realname = $.trim($("#Realname").val());
|
||||
var comment = $.trim($("#Comment").val());
|
||||
var username = $.trim($("#Username").val());
|
||||
var email = $.trim($("#Email").val());
|
||||
var password = $.trim($("#Password").val());
|
||||
var confirmedPassword = $.trim($("#ConfirmedPassword").val());
|
||||
var realname = $.trim($("#Realname").val());
|
||||
var comment = $.trim($("#Comment").val());
|
||||
var isAdmin = $("#isAdmin").val();
|
||||
|
||||
$.ajax({
|
||||
url : '/signUp',
|
||||
data:{username: username, password: password, realname: realname, comment: comment, email: email},
|
||||
@ -47,10 +49,14 @@ jQuery(function(){
|
||||
if(xhr && xhr.status == 200){
|
||||
$("#dlgModal")
|
||||
.dialogModal({
|
||||
"title": i18n.getMessage("title_sign_up"),
|
||||
"content": i18n.getMessage("registered_successfully"),
|
||||
"callback": function(){
|
||||
document.location = "/signIn";
|
||||
"title": isAdmin == "true" ? i18n.getMessage("title_add_user") : i18n.getMessage("title_sign_up"),
|
||||
"content": isAdmin == "true" ? i18n.getMessage("added_user_successfully") : i18n.getMessage("registered_successfully"),
|
||||
"callback": function(){
|
||||
if(isAdmin == "true") {
|
||||
document.location = "/registry/project";
|
||||
}else{
|
||||
document.location = "/signIn";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -27,6 +27,8 @@ import (
|
||||
var logger = New(os.Stdout, NewTextFormatter(), WarningLevel)
|
||||
|
||||
func init() {
|
||||
logger.callDepth = 3
|
||||
|
||||
// TODO add item in configuaration file
|
||||
lvl := os.Getenv("LOG_LEVEL")
|
||||
if len(lvl) == 0 {
|
||||
@ -46,18 +48,20 @@ func init() {
|
||||
|
||||
// Logger provides a struct with fields that describe the details of logger.
|
||||
type Logger struct {
|
||||
out io.Writer
|
||||
fmtter Formatter
|
||||
lvl Level
|
||||
mu sync.Mutex
|
||||
out io.Writer
|
||||
fmtter Formatter
|
||||
lvl Level
|
||||
callDepth int
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// New returns a customized Logger
|
||||
func New(out io.Writer, fmtter Formatter, lvl Level) *Logger {
|
||||
return &Logger{
|
||||
out: out,
|
||||
fmtter: fmtter,
|
||||
lvl: lvl,
|
||||
out: out,
|
||||
fmtter: fmtter,
|
||||
lvl: lvl,
|
||||
callDepth: 2,
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,7 +121,7 @@ func (l *Logger) output(record *Record) (err error) {
|
||||
// Debug ...
|
||||
func (l *Logger) Debug(v ...interface{}) {
|
||||
if l.lvl <= DebugLevel {
|
||||
line := line(2)
|
||||
line := line(l.callDepth)
|
||||
record := NewRecord(time.Now(), fmt.Sprint(v...), line, DebugLevel)
|
||||
l.output(record)
|
||||
}
|
||||
@ -126,7 +130,7 @@ func (l *Logger) Debug(v ...interface{}) {
|
||||
// Debugf ...
|
||||
func (l *Logger) Debugf(format string, v ...interface{}) {
|
||||
if l.lvl <= DebugLevel {
|
||||
line := line(2)
|
||||
line := line(l.callDepth)
|
||||
record := NewRecord(time.Now(), fmt.Sprintf(format, v...), line, DebugLevel)
|
||||
l.output(record)
|
||||
}
|
||||
@ -167,7 +171,7 @@ func (l *Logger) Warningf(format string, v ...interface{}) {
|
||||
// Error ...
|
||||
func (l *Logger) Error(v ...interface{}) {
|
||||
if l.lvl <= ErrorLevel {
|
||||
line := line(2)
|
||||
line := line(l.callDepth)
|
||||
record := NewRecord(time.Now(), fmt.Sprint(v...), line, ErrorLevel)
|
||||
l.output(record)
|
||||
}
|
||||
@ -176,7 +180,7 @@ func (l *Logger) Error(v ...interface{}) {
|
||||
// Errorf ...
|
||||
func (l *Logger) Errorf(format string, v ...interface{}) {
|
||||
if l.lvl <= ErrorLevel {
|
||||
line := line(2)
|
||||
line := line(l.callDepth)
|
||||
record := NewRecord(time.Now(), fmt.Sprintf(format, v...), line, ErrorLevel)
|
||||
l.output(record)
|
||||
}
|
||||
@ -185,7 +189,7 @@ func (l *Logger) Errorf(format string, v ...interface{}) {
|
||||
// Fatal ...
|
||||
func (l *Logger) Fatal(v ...interface{}) {
|
||||
if l.lvl <= FatalLevel {
|
||||
line := line(2)
|
||||
line := line(l.callDepth)
|
||||
record := NewRecord(time.Now(), fmt.Sprint(v...), line, FatalLevel)
|
||||
l.output(record)
|
||||
}
|
||||
@ -195,7 +199,7 @@ func (l *Logger) Fatal(v ...interface{}) {
|
||||
// Fatalf ...
|
||||
func (l *Logger) Fatalf(format string, v ...interface{}) {
|
||||
if l.lvl <= FatalLevel {
|
||||
line := line(2)
|
||||
line := line(l.callDepth)
|
||||
record := NewRecord(time.Now(), fmt.Sprintf(format, v...), line, FatalLevel)
|
||||
l.output(record)
|
||||
}
|
||||
@ -259,5 +263,12 @@ func line(calldepth int) string {
|
||||
line = 0
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s:%d", file, line)
|
||||
for i := len(file) - 2; i > 0; i-- {
|
||||
if file[i] == os.PathSeparator {
|
||||
file = file[i+1:]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("[%s:%d]:", file, line)
|
||||
}
|
||||
|
@ -16,7 +16,11 @@
|
||||
<div class="col-sm-4"></div>
|
||||
<div class="col-sm-4">
|
||||
<div class="page-header">
|
||||
{{ if eq .IsAdmin true }}
|
||||
<h1>{{i18n .Lang "add_user" }}</h1>
|
||||
{{ else }}
|
||||
<h1>{{i18n .Lang "registration"}}</h1>
|
||||
{{ end }}
|
||||
</div>
|
||||
<form class="form">
|
||||
<div class="alert alert-danger" role="alert" id="divErrMsg"></div>
|
||||
@ -62,7 +66,13 @@
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<div class="text-center">
|
||||
<button type="button" class="btn btn-default" id="btnPageSignUp">{{i18n .Lang "sign_up"}}</button>
|
||||
<button type="button" class="btn btn-default" id="btnPageSignUp">
|
||||
{{ if eq .IsAdmin true }}
|
||||
{{i18n .Lang "add_user" }}
|
||||
{{ else }}
|
||||
{{i18n .Lang "sign_up"}}
|
||||
{{ end }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -13,6 +13,7 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
<input type="hidden" id="currentLanguage" value="{{.Lang}}">
|
||||
<input type="hidden" id="isAdmin" value="{{.IsAdmin}}">
|
||||
<nav class="navbar navbar-default" role="navigation" style="margin-bottom: 0;">
|
||||
<div class="navbar-header">
|
||||
<button aria-controls="navbar" aria-expanded="false" data-target="#navbar" data-toggle="collapse" class="navbar-toggle collapsed" type="button">
|
||||
@ -55,6 +56,11 @@
|
||||
<li><a id="aChangePassword" href="/changePassword" target="_blank"><span class="glyphicon glyphicon-pencil"></span> {{i18n .Lang "change_password"}}</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
{{ end }}
|
||||
{{ if eq .AuthMode "db_auth" }}
|
||||
{{ if eq .IsAdmin true }}
|
||||
<li><a id="aAddUser" href="/addUser" target="_blank"><span class="glyphicon glyphicon-plus"></span> {{i18n .Lang "add_user"}}</a></li>
|
||||
{{ end }}
|
||||
{{ end}}
|
||||
<li><a id="aLogout" href="#"><span class="glyphicon glyphicon-log-in"></span> {{i18n .Lang "log_out"}}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@ -63,7 +69,9 @@
|
||||
{{ else if eq .AuthMode "db_auth" }}
|
||||
<div class="input-group">
|
||||
<button type="button" class="btn btn-default" id="btnSignIn">{{i18n .Lang "sign_in"}}</button>
|
||||
{{ if eq .SelfRegistration true }}
|
||||
<button type="button" class="btn btn-success" id="btnSignUp">{{i18n .Lang "sign_up"}}</button>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ else }}
|
||||
<div class="input-group">
|
||||
|
Loading…
Reference in New Issue
Block a user