fix issue in golint, support project creation restriction at backend

This commit is contained in:
Tan Jiang 2016-11-16 20:31:04 +08:00
parent 33da8b230e
commit 9d7a18a0a3
19 changed files with 120 additions and 109 deletions

View File

@ -26,4 +26,4 @@ EXT_ENDPOINT=$ui_url
TOKEN_ENDPOINT=http://ui TOKEN_ENDPOINT=http://ui
VERIFY_REMOTE_CERT=$verify_remote_cert VERIFY_REMOTE_CERT=$verify_remote_cert
TOKEN_EXPIRATION=$token_expiration TOKEN_EXPIRATION=$token_expiration
CREATE_PROJECT_RESTRICTION=$create_project_restriction PROJECT_CREATION_RESTRICTION=$project_creation_restriction

View File

@ -83,6 +83,9 @@ crt_organizationalunit = organizational unit
crt_commonname = example.com crt_commonname = example.com
crt_email = example@example.com crt_email = example@example.com
#The flag to control what users have permission to create projects
#Be default everyone can create a project, set to "adminonly" such that only admin can create project.
project_creation_restriction = adminonly
#The path of cert and key files for nginx, they are applied only the protocol is set to https #The path of cert and key files for nginx, they are applied only the protocol is set to https
ssl_cert = /data/server.crt ssl_cert = /data/server.crt

View File

@ -32,6 +32,10 @@ def validate(conf):
cert_key_path = rcp.get("configuration", "ssl_cert_key") cert_key_path = rcp.get("configuration", "ssl_cert_key")
if not os.path.isfile(cert_key_path): if not os.path.isfile(cert_key_path):
raise Exception("Error: The path for certificate key: %s is invalid" % cert_key_path) raise Exception("Error: The path for certificate key: %s is invalid" % cert_key_path)
project_creation = rcp.get("configuration", "project_creation_restriction")
if project_creation != "everyone" and project_creation != "adminonly":
raise Exception("Error invalid value for project_creation_restriction: %s" % project_creation)
def get_secret_key(path): def get_secret_key(path):
key_file = os.path.join(path, "secretkey") key_file = os.path.join(path, "secretkey")
@ -115,6 +119,7 @@ crt_email = rcp.get("configuration", "crt_email")
max_job_workers = rcp.get("configuration", "max_job_workers") max_job_workers = rcp.get("configuration", "max_job_workers")
token_expiration = rcp.get("configuration", "token_expiration") token_expiration = rcp.get("configuration", "token_expiration")
verify_remote_cert = rcp.get("configuration", "verify_remote_cert") verify_remote_cert = rcp.get("configuration", "verify_remote_cert")
proj_cre_restriction = rcp.get("configuration", "project_creation_restriction")
#secret_key = rcp.get("configuration", "secret_key") #secret_key = rcp.get("configuration", "secret_key")
secret_key = get_secret_key(args.data_volume) secret_key = get_secret_key(args.data_volume)
######## ########
@ -200,6 +205,7 @@ render(os.path.join(templates_dir, "ui", "env"),
ui_secret=ui_secret, ui_secret=ui_secret,
secret_key=secret_key, secret_key=secret_key,
verify_remote_cert=verify_remote_cert, verify_remote_cert=verify_remote_cert,
project_creation_restriction=proj_cre_restriction,
token_expiration=token_expiration) token_expiration=token_expiration)
render(os.path.join(templates_dir, "ui", "app.conf"), render(os.path.join(templates_dir, "ui", "app.conf"),

View File

@ -12,6 +12,8 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// Package config provide methods to get the configurations reqruied by code in src/common
package config package config
import ( import (
@ -20,8 +22,8 @@ import (
"strings" "strings"
) )
// ConfigLoader is the interface to load configurations // ConfLoader is the interface to load configurations
type ConfigLoader interface { type ConfLoader interface {
// Load will load configuration from different source into a string map, the values in the map will be parsed in to configurations. // Load will load configuration from different source into a string map, the values in the map will be parsed in to configurations.
Load() (map[string]string, error) Load() (map[string]string, error)
@ -41,18 +43,22 @@ func (ec *EnvConfigLoader) Load() (map[string]string, error) {
return m, nil return m, nil
} }
// ConfigParser // ConfParser ...
type ConfigParser interface { type ConfParser interface {
//Parse parse the input raw map into a config map //Parse parse the input raw map into a config map
Parse(raw map[string]string, config map[string]interface{}) error Parse(raw map[string]string, config map[string]interface{}) error
} }
// Config wraps a map for the processed configuration values,
// and loader parser to read configuration from external source and process the values.
type Config struct { type Config struct {
Config map[string]interface{} Config map[string]interface{}
Loader ConfigLoader Loader ConfLoader
Parser ConfigParser Parser ConfParser
} }
// Load reload the configurations
func (conf *Config) Load() error { func (conf *Config) Load() error {
rawMap, err := conf.Loader.Load() rawMap, err := conf.Loader.Load()
if err != nil { if err != nil {
@ -62,6 +68,7 @@ func (conf *Config) Load() error {
return err return err
} }
// MySQLSetting wraps the settings of a MySQL DB
type MySQLSetting struct { type MySQLSetting struct {
Database string Database string
User string User string
@ -70,6 +77,7 @@ type MySQLSetting struct {
Port string Port string
} }
// SQLiteSetting wraps the settings of a SQLite DB
type SQLiteSetting struct { type SQLiteSetting struct {
FilePath string FilePath string
} }
@ -165,6 +173,7 @@ func TokenEndpoint() string {
return commonConfig.Config["token_endpoint"].(string) return commonConfig.Config["token_endpoint"].(string)
} }
// LogLevel returns the log level in string format.
func LogLevel() string { func LogLevel() string {
return commonConfig.Config["log_level"].(string) return commonConfig.Config["log_level"].(string)
} }

View File

@ -54,7 +54,7 @@ func getDatabase() (db Database, err error) {
switch config.Database() { switch config.Database() {
case "", "mysql": case "", "mysql":
db = NewMySQL(config.MySQL().Host, config.MySQL().Port, config.MySQL().User, db = NewMySQL(config.MySQL().Host, config.MySQL().Port, config.MySQL().User,
config.MySQL().Port, config.MySQL().Database) config.MySQL().Password, config.MySQL().Database)
case "sqlite": case "sqlite":
db = NewSQLite(config.SQLite().FilePath) db = NewSQLite(config.SQLite().FilePath)
default: default:

View File

@ -24,6 +24,7 @@ import (
"github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/config"
"strconv" "strconv"
"time" "time"
@ -72,11 +73,19 @@ func (p *ProjectAPI) Prepare() {
// Post ... // Post ...
func (p *ProjectAPI) Post() { func (p *ProjectAPI) Post() {
p.userID = p.ValidateUser() p.userID = p.ValidateUser()
isSysAdmin, err := dao.IsAdminRole(p.userID)
if err != nil {
log.Errorf("Failed to check admin role: %v", err)
}
if !isSysAdmin && config.OnlyAdminCreateProject() {
log.Errorf("Only sys admin can create project")
p.RenderError(http.StatusForbidden, "Only system admin can create project")
return
}
var req projectReq var req projectReq
p.DecodeJSONReq(&req) p.DecodeJSONReq(&req)
public := req.Public public := req.Public
err := validateProjectReq(req) err = validateProjectReq(req)
if err != nil { if err != nil {
log.Errorf("Invalid project request, error: %v", err) log.Errorf("Invalid project request, error: %v", err)
p.RenderError(http.StatusBadRequest, fmt.Sprintf("invalid request: %v", err)) p.RenderError(http.StatusBadRequest, fmt.Sprintf("invalid request: %v", err))

View File

@ -19,23 +19,23 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os"
"sort" "sort"
"github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/manifest/schema2" "github.com/docker/distribution/manifest/schema2"
"github.com/vmware/harbor/src/common/api"
"github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/common/utils/registry"
"github.com/vmware/harbor/src/ui/service/cache" "github.com/vmware/harbor/src/ui/service/cache"
svc_utils "github.com/vmware/harbor/src/ui/service/utils" svc_utils "github.com/vmware/harbor/src/ui/service/utils"
"github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/common/api"
"github.com/vmware/harbor/src/common/utils/registry"
registry_error "github.com/vmware/harbor/src/common/utils/registry/error" registry_error "github.com/vmware/harbor/src/common/utils/registry/error"
"github.com/vmware/harbor/src/common/utils" "github.com/vmware/harbor/src/common/utils"
"github.com/vmware/harbor/src/common/utils/registry/auth" "github.com/vmware/harbor/src/common/utils/registry/auth"
"github.com/vmware/harbor/src/ui/config"
) )
// RepositoryAPI handles request to /api/repositories /api/repositories/tags /api/repositories/manifests, the parm has to be put // RepositoryAPI handles request to /api/repositories /api/repositories/tags /api/repositories/manifests, the parm has to be put
@ -361,7 +361,7 @@ func (ra *RepositoryAPI) GetManifests() {
} }
func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repository, err error) { func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repository, err error) {
endpoint := os.Getenv("REGISTRY_URL") endpoint := config.InternalRegistryURL()
username, password, ok := ra.Ctx.Request.BasicAuth() username, password, ok := ra.Ctx.Request.BasicAuth()
if ok { if ok {

View File

@ -20,17 +20,17 @@ import (
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"os"
"strconv" "strconv"
"github.com/vmware/harbor/src/common/api"
"github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils" "github.com/vmware/harbor/src/common/utils"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/common/api"
"github.com/vmware/harbor/src/common/utils/registry" "github.com/vmware/harbor/src/common/utils/registry"
"github.com/vmware/harbor/src/common/utils/registry/auth" "github.com/vmware/harbor/src/common/utils/registry/auth"
registry_error "github.com/vmware/harbor/src/common/utils/registry/error" registry_error "github.com/vmware/harbor/src/common/utils/registry/error"
"github.com/vmware/harbor/src/ui/config"
) )
// TargetAPI handles request to /api/targets/ping /api/targets/{} // TargetAPI handles request to /api/targets/ping /api/targets/{}
@ -41,8 +41,7 @@ type TargetAPI struct {
// Prepare validates the user // Prepare validates the user
func (t *TargetAPI) Prepare() { func (t *TargetAPI) Prepare() {
//TODO:move to config t.secretKey = config.SecretKey()
t.secretKey = os.Getenv("SECRET_KEY")
userID := t.ValidateUser() userID := t.ValidateUser()
isSysAdmin, err := dao.IsAdminRole(userID) isSysAdmin, err := dao.IsAdminRole(userID)

View File

@ -18,7 +18,6 @@ package api
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"os"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@ -27,6 +26,7 @@ import (
"github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/config"
) )
// UserAPI handles request to /api/users/{} // UserAPI handles request to /api/users/{}
@ -47,16 +47,9 @@ type passwordReq struct {
// Prepare validates the URL and parms // Prepare validates the URL and parms
func (ua *UserAPI) Prepare() { func (ua *UserAPI) Prepare() {
authMode := strings.ToLower(os.Getenv("AUTH_MODE")) ua.AuthMode = config.AuthMode()
if authMode == "" {
authMode = "db_auth"
}
ua.AuthMode = authMode
selfRegistration := strings.ToLower(os.Getenv("SELF_REGISTRATION")) ua.SelfRegistration = config.SelfRegistration()
if selfRegistration == "on" {
ua.SelfRegistration = true
}
if ua.Ctx.Input.IsPost() { if ua.Ctx.Input.IsPost() {
sessionUserID := ua.GetSession("userId") sessionUserID := ua.GetSession("userId")
@ -241,9 +234,7 @@ func (ua *UserAPI) Delete() {
return return
} }
// TODO read from conifg if config.AuthMode() == "ldap_auth" {
authMode := os.Getenv("AUTH_MODE")
if authMode == "ldap_auth" {
ua.CustomAbort(http.StatusForbidden, "user can not be deleted in LDAP authentication mode") ua.CustomAbort(http.StatusForbidden, "user can not be deleted in LDAP authentication mode")
} }

View File

@ -22,18 +22,18 @@ import (
"io/ioutil" "io/ioutil"
"net" "net"
"net/http" "net/http"
"os"
"sort" "sort"
"strings" "strings"
"time" "time"
"github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/ui/service/cache"
"github.com/vmware/harbor/src/common/utils" "github.com/vmware/harbor/src/common/utils"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/common/utils/registry" "github.com/vmware/harbor/src/common/utils/registry"
registry_error "github.com/vmware/harbor/src/common/utils/registry/error" registry_error "github.com/vmware/harbor/src/common/utils/registry/error"
"github.com/vmware/harbor/src/ui/config"
"github.com/vmware/harbor/src/ui/service/cache"
) )
func checkProjectPermission(userID int, projectID int64) bool { func checkProjectPermission(userID int, projectID int64) bool {
@ -234,8 +234,7 @@ func addAuthentication(req *http.Request) {
if req != nil { if req != nil {
req.AddCookie(&http.Cookie{ req.AddCookie(&http.Cookie{
Name: models.UISecretCookie, Name: models.UISecretCookie,
// TODO read secret from config Value: config.UISecret(),
Value: os.Getenv("UI_SECRET"),
}) })
} }
} }
@ -351,8 +350,7 @@ func diffRepos(reposInRegistry []string, reposInDB []string) ([]string, []string
} }
// TODO remove the workaround when the bug of registry is fixed // TODO remove the workaround when the bug of registry is fixed
// TODO read it from config endpoint := config.InternalRegistryURL()
endpoint := os.Getenv("REGISTRY_URL")
client, err := cache.NewRepositoryClient(endpoint, true, client, err := cache.NewRepositoryClient(endpoint, true,
"admin", repoInR, "repository", repoInR) "admin", repoInR, "repository", repoInR)
if err != nil { if err != nil {
@ -374,8 +372,7 @@ func diffRepos(reposInRegistry []string, reposInDB []string) ([]string, []string
j++ j++
} else { } else {
// TODO remove the workaround when the bug of registry is fixed // TODO remove the workaround when the bug of registry is fixed
// TODO read it from config endpoint := config.InternalRegistryURL()
endpoint := os.Getenv("REGISTRY_URL")
client, err := cache.NewRepositoryClient(endpoint, true, client, err := cache.NewRepositoryClient(endpoint, true,
"admin", repoInR, "repository", repoInR) "admin", repoInR, "repository", repoInR)
if err != nil { if err != nil {
@ -425,7 +422,7 @@ func projectExists(repository string) (bool, error) {
} }
func initRegistryClient() (r *registry.Registry, err error) { func initRegistryClient() (r *registry.Registry, err error) {
endpoint := os.Getenv("REGISTRY_URL") endpoint := config.InternalRegistryURL()
addr := endpoint addr := endpoint
if strings.Contains(endpoint, "/") { if strings.Contains(endpoint, "/") {
@ -462,32 +459,20 @@ func initRegistryClient() (r *registry.Registry, err error) {
} }
func buildReplicationURL() string { func buildReplicationURL() string {
url := getJobServiceURL() url := config.InternalJobServiceURL()
return fmt.Sprintf("%s/api/jobs/replication", url) return fmt.Sprintf("%s/api/jobs/replication", url)
} }
func buildJobLogURL(jobID string) string { func buildJobLogURL(jobID string) string {
url := getJobServiceURL() url := config.InternalJobServiceURL()
return fmt.Sprintf("%s/api/jobs/replication/%s/log", url, jobID) return fmt.Sprintf("%s/api/jobs/replication/%s/log", url, jobID)
} }
func buildReplicationActionURL() string { func buildReplicationActionURL() string {
url := getJobServiceURL() url := config.InternalJobServiceURL()
return fmt.Sprintf("%s/api/jobs/replication/actions", url) return fmt.Sprintf("%s/api/jobs/replication/actions", url)
} }
func getJobServiceURL() string {
url := os.Getenv("JOB_SERVICE_URL")
url = strings.TrimSpace(url)
url = strings.TrimRight(url, "/")
if len(url) == 0 {
url = "http://jobservice"
}
return url
}
func getReposByProject(name string, keyword ...string) ([]string, error) { func getReposByProject(name string, keyword ...string) ([]string, error) {
repositories := []string{} repositories := []string{}

View File

@ -18,14 +18,14 @@ package ldap
import ( import (
"errors" "errors"
"fmt" "fmt"
"os"
"strings" "strings"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/auth"
"github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/ui/auth"
"github.com/vmware/harbor/src/ui/config"
"github.com/mqu/openldap" "github.com/mqu/openldap"
) )
@ -46,7 +46,7 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
return nil, fmt.Errorf("the principal contains meta char: %q", c) return nil, fmt.Errorf("the principal contains meta char: %q", c)
} }
} }
ldapURL := os.Getenv("LDAP_URL") ldapURL := config.LDAP().URL
if ldapURL == "" { if ldapURL == "" {
return nil, errors.New("can not get any available LDAP_URL") return nil, errors.New("can not get any available LDAP_URL")
} }
@ -57,16 +57,16 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
} }
ldap.SetOption(openldap.LDAP_OPT_PROTOCOL_VERSION, openldap.LDAP_VERSION3) ldap.SetOption(openldap.LDAP_OPT_PROTOCOL_VERSION, openldap.LDAP_VERSION3)
ldapBaseDn := os.Getenv("LDAP_BASE_DN") ldapBaseDn := config.LDAP().BaseDn
if ldapBaseDn == "" { if ldapBaseDn == "" {
return nil, errors.New("can not get any available LDAP_BASE_DN") return nil, errors.New("can not get any available LDAP_BASE_DN")
} }
log.Debug("baseDn:", ldapBaseDn) log.Debug("baseDn:", ldapBaseDn)
ldapSearchDn := os.Getenv("LDAP_SEARCH_DN") ldapSearchDn := config.LDAP().SearchDn
if ldapSearchDn != "" { if ldapSearchDn != "" {
log.Debug("Search DN: ", ldapSearchDn) log.Debug("Search DN: ", ldapSearchDn)
ldapSearchPwd := os.Getenv("LDAP_SEARCH_PWD") ldapSearchPwd := config.LDAP().SearchPwd
err = ldap.Bind(ldapSearchDn, ldapSearchPwd) err = ldap.Bind(ldapSearchDn, ldapSearchPwd)
if err != nil { if err != nil {
log.Debug("Bind search dn error", err) log.Debug("Bind search dn error", err)
@ -74,8 +74,8 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
} }
} }
attrName := os.Getenv("LDAP_UID") attrName := config.LDAP().UID
filter := os.Getenv("LDAP_FILTER") filter := config.LDAP().Filter
if filter != "" { if filter != "" {
filter = "(&" + filter + "(" + attrName + "=" + m.Principal + "))" filter = "(&" + filter + "(" + attrName + "=" + m.Principal + "))"
} else { } else {
@ -83,7 +83,7 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
} }
log.Debug("one or more filter", filter) log.Debug("one or more filter", filter)
ldapScope := os.Getenv("LDAP_SCOPE") ldapScope := config.LDAP().Scope
var scope int var scope int
if ldapScope == "1" { if ldapScope == "1" {
scope = openldap.LDAP_SCOPE_BASE scope = openldap.LDAP_SCOPE_BASE

View File

@ -12,6 +12,8 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// Package config provides methods to get configurations required by code in src/ui
package config package config
import ( import (
@ -22,6 +24,7 @@ import (
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
) )
// LDAPSetting wraps the setting of an LDAP server
type LDAPSetting struct { type LDAPSetting struct {
URL string URL string
BaseDn string BaseDn string
@ -50,7 +53,7 @@ func (up *uiParser) Parse(raw map[string]string, config map[string]interface{})
config["ldap"] = setting config["ldap"] = setting
} }
config["auth_mode"] = mode config["auth_mode"] = mode
var tokenExpiration int = 30 //minutes var tokenExpiration = 30 //minutes
if len(raw["TOKEN_EXPIRATION"]) > 0 { if len(raw["TOKEN_EXPIRATION"]) > 0 {
i, err := strconv.Atoi(raw["TOKEN_EXPIRATION"]) i, err := strconv.Atoi(raw["TOKEN_EXPIRATION"])
if err != nil { if err != nil {
@ -67,7 +70,7 @@ func (up *uiParser) Parse(raw map[string]string, config map[string]interface{})
config["ui_secret"] = raw["UI_SECRET"] config["ui_secret"] = raw["UI_SECRET"]
config["secret_key"] = raw["SECRET_KEY"] config["secret_key"] = raw["SECRET_KEY"]
config["self_registration"] = raw["SELF_REGISTRATION"] != "off" config["self_registration"] = raw["SELF_REGISTRATION"] != "off"
config["admin_create_project"] = strings.ToLower(raw["PROJECT_CREATE_RESTRICTION"]) == "adminonly" config["admin_create_project"] = strings.ToLower(raw["PROJECT_CREATION_RESTRICTION"]) == "adminonly"
registryURL := raw["REGISTRY_URL"] registryURL := raw["REGISTRY_URL"]
registryURL = strings.TrimRight(registryURL, "/") registryURL = strings.TrimRight(registryURL, "/")
config["internal_registry_url"] = registryURL config["internal_registry_url"] = registryURL
@ -80,7 +83,7 @@ func (up *uiParser) Parse(raw map[string]string, config map[string]interface{})
var uiConfig *commonConfig.Config var uiConfig *commonConfig.Config
func init() { func init() {
uiKeys := []string{"AUTH_MODE", "LDAP_URL", "LDAP_BASE_DN", "LDAP_SEARCH_DN", "LDAP_SEARCH_PWD", "LDAP_UID", "LDAP_FILTER", "LDAP_SCOPE", "TOKEN_EXPIRATION", "HARBOR_ADMIN_PASSWORD", "EXT_REG_URL", "UI_SECRET", "SECRET_KEY", "SELF_REGISTRATION", "PROJECT_CREATE_RESTRICTION", "REGISTRY_URL", "JOB_SERVICE_URL"} uiKeys := []string{"AUTH_MODE", "LDAP_URL", "LDAP_BASE_DN", "LDAP_SEARCH_DN", "LDAP_SEARCH_PWD", "LDAP_UID", "LDAP_FILTER", "LDAP_SCOPE", "TOKEN_EXPIRATION", "HARBOR_ADMIN_PASSWORD", "EXT_REG_URL", "UI_SECRET", "SECRET_KEY", "SELF_REGISTRATION", "PROJECT_CREATION_RESTRICTION", "REGISTRY_URL", "JOB_SERVICE_URL"}
uiConfig = &commonConfig.Config{ uiConfig = &commonConfig.Config{
Config: make(map[string]interface{}), Config: make(map[string]interface{}),
Loader: &commonConfig.EnvConfigLoader{Keys: uiKeys}, Loader: &commonConfig.EnvConfigLoader{Keys: uiKeys},
@ -91,45 +94,62 @@ func init() {
} }
} }
// Reload ...
func Reload() error { func Reload() error {
return uiConfig.Load() return uiConfig.Load()
} }
// AuthMode ...
func AuthMode() string { func AuthMode() string {
return uiConfig.Config["auth_mode"].(string) return uiConfig.Config["auth_mode"].(string)
} }
// LDAP returns the setting of ldap server
func LDAP() LDAPSetting { func LDAP() LDAPSetting {
return uiConfig.Config["ldap"].(LDAPSetting) return uiConfig.Config["ldap"].(LDAPSetting)
} }
// TokenExpiration returns the token expiration time (in minute)
func TokenExpiration() int { func TokenExpiration() int {
return uiConfig.Config["token_exp"].(int) return uiConfig.Config["token_exp"].(int)
} }
// ExtRegistryURL returns the registry URL to exposed to external client
func ExtRegistryURL() string { func ExtRegistryURL() string {
return uiConfig.Config["ext_reg_url"].(string) return uiConfig.Config["ext_reg_url"].(string)
} }
// UISecret returns the value of UI secret cookie, used for communication between UI and JobService
func UISecret() string { func UISecret() string {
return uiConfig.Config["ui_secret"].(string) return uiConfig.Config["ui_secret"].(string)
} }
// SecretKey returns the secret key to encrypt the password of target
func SecretKey() string { func SecretKey() string {
return uiConfig.Config["secret_key"].(string) return uiConfig.Config["secret_key"].(string)
} }
// SelfRegistration returns the enablement of self registration
func SelfRegistration() bool { func SelfRegistration() bool {
return uiConfig.Config["self_registration"].(bool) return uiConfig.Config["self_registration"].(bool)
} }
// InternalRegistryURL returns registry URL for internal communication between Harbor containers
func InternalRegistryURL() string { func InternalRegistryURL() string {
return uiConfig.Config["internal_registry_url"].(string) return uiConfig.Config["internal_registry_url"].(string)
} }
// InternalJobServiceURL returns jobservice URL for internal communication between Harbor containers
func InternalJobServiceURL() string { func InternalJobServiceURL() string {
return uiConfig.Config["internal_jobservice_url"].(string) return uiConfig.Config["internal_jobservice_url"].(string)
} }
// InitialAdminPassword returns the initial password for administrator
func InitialAdminPassword() string {
return uiConfig.Config["admin_password"].(string)
}
// OnlyAdminCreateProject returns the flag to restrict that only sys admin can create project
func OnlyAdminCreateProject() bool { func OnlyAdminCreateProject() bool {
return uiConfig.Config["admin_create_project"].(bool) return uiConfig.Config["admin_create_project"].(bool)
} }

View File

@ -37,7 +37,7 @@ var (
uiSecret = "ffadsdfsdf" uiSecret = "ffadsdfsdf"
secretKey = "keykey" secretKey = "keykey"
selfRegistration = "off" selfRegistration = "off"
projectCreationRestriction = "everyone" projectCreationRestriction = "adminonly"
internalRegistryURL = "http://registry:5000" internalRegistryURL = "http://registry:5000"
jobServiceURL = "http://jobservice" jobServiceURL = "http://jobservice"
) )
@ -58,7 +58,7 @@ func TestMain(m *testing.M) {
os.Setenv("UI_SECRET", uiSecret) os.Setenv("UI_SECRET", uiSecret)
os.Setenv("SECRET_KEY", secretKey) os.Setenv("SECRET_KEY", secretKey)
os.Setenv("SELF_REGISTRATION", selfRegistration) os.Setenv("SELF_REGISTRATION", selfRegistration)
os.Setenv("CREATE_PROJECT_RESTRICTION", projectCreationRestriction) os.Setenv("PROJECT_CREATION_RESTRICTION", projectCreationRestriction)
os.Setenv("REGISTRY_URL", internalRegistryURL) os.Setenv("REGISTRY_URL", internalRegistryURL)
os.Setenv("JOB_SERVICE_URL", jobServiceURL) os.Setenv("JOB_SERVICE_URL", jobServiceURL)
@ -132,7 +132,13 @@ func TestSecrets(t *testing.T) {
} }
func TestProjectCreationRestrict(t *testing.T) { func TestProjectCreationRestrict(t *testing.T) {
if OnlyAdminCreateProject() { if !OnlyAdminCreateProject() {
t.Errorf("Expected OnlyAdminCreateProject to be false") t.Errorf("Expected OnlyAdminCreateProject to be true")
}
}
func TestInitAdminPassword(t *testing.T) {
if InitialAdminPassword() != adminPassword {
t.Errorf("Expected adminPassword: %s, in fact: %s", adminPassword, InitialAdminPassword)
} }
} }

View File

@ -12,6 +12,7 @@ import (
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/auth" "github.com/vmware/harbor/src/ui/auth"
"github.com/vmware/harbor/src/ui/config"
) )
// BaseController wraps common methods such as i18n support, forward, which can be leveraged by other UI render controllers. // BaseController wraps common methods such as i18n support, forward, which can be leveraged by other UI render controllers.
@ -99,7 +100,7 @@ func (b *BaseController) Prepare() {
b.Data["CurLang"] = curLang.Name b.Data["CurLang"] = curLang.Name
b.Data["RestLangs"] = restLangs b.Data["RestLangs"] = restLangs
authMode := strings.ToLower(os.Getenv("AUTH_MODE")) authMode := config.AuthMode()
if authMode == "" { if authMode == "" {
authMode = "db_auth" authMode = "db_auth"
} }
@ -116,11 +117,9 @@ func (b *BaseController) Prepare() {
b.UseCompressedJS = false b.UseCompressedJS = false
} }
selfRegistration := strings.ToLower(os.Getenv("SELF_REGISTRATION")) b.SelfRegistration = config.SelfRegistration()
if selfRegistration == "on" {
b.SelfRegistration = true b.Data["SelfRegistration"] = config.SelfRegistration()
}
b.Data["SelfRegistration"] = b.SelfRegistration
} }
// Forward to setup layout and template for content for a page. // Forward to setup layout and template for content for a page.

View File

@ -3,7 +3,6 @@ package controllers
import ( import (
"bytes" "bytes"
"net/http" "net/http"
"os"
"regexp" "regexp"
"text/template" "text/template"

View File

@ -1,6 +1,8 @@
package controllers package controllers
import "os" import (
"github.com/vmware/harbor/src/ui/config"
)
// RepositoryController handles request to /repository // RepositoryController handles request to /repository
type RepositoryController struct { type RepositoryController struct {
@ -9,6 +11,6 @@ type RepositoryController struct {
// Get renders repository page // Get renders repository page
func (rc *RepositoryController) Get() { func (rc *RepositoryController) Get() {
rc.Data["HarborRegUrl"] = os.Getenv("HARBOR_REG_URL") rc.Data["HarborRegUrl"] = config.ExtRegistryURL()
rc.Forward("page_title_repository", "repository.htm") rc.Forward("page_title_repository", "repository.htm")
} }

View File

@ -25,11 +25,12 @@ import (
"github.com/astaxie/beego" "github.com/astaxie/beego"
_ "github.com/astaxie/beego/session/redis" _ "github.com/astaxie/beego/session/redis"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/ui/api" "github.com/vmware/harbor/src/ui/api"
_ "github.com/vmware/harbor/src/ui/auth/db" _ "github.com/vmware/harbor/src/ui/auth/db"
_ "github.com/vmware/harbor/src/ui/auth/ldap" _ "github.com/vmware/harbor/src/ui/auth/ldap"
"github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/ui/config"
"github.com/vmware/harbor/src/common/models"
) )
const ( const (
@ -76,7 +77,7 @@ func main() {
dao.InitDatabase() dao.InitDatabase()
if err := updateInitPassword(adminUserID, os.Getenv("HARBOR_ADMIN_PASSWORD")); err != nil { if err := updateInitPassword(adminUserID, config.InitialAdminPassword()); err != nil {
log.Error(err) log.Error(err)
} }
initRouters() initRouters()

View File

@ -21,13 +21,12 @@ import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os"
"strconv"
"strings" "strings"
"time" "time"
"github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/config"
"github.com/docker/distribution/registry/auth/token" "github.com/docker/distribution/registry/auth/token"
"github.com/docker/libtrust" "github.com/docker/libtrust"
@ -38,27 +37,10 @@ const (
privateKey = "/etc/ui/private_key.pem" privateKey = "/etc/ui/private_key.pem"
) )
var ( var expiration int //minutes
expiration = 30 //minutes
)
func init() { func init() {
// TODO read it from config expiration = config.TokenExpiration()
expi := os.Getenv("TOKEN_EXPIRATION")
if len(expi) != 0 {
i, err := strconv.Atoi(expi)
if err != nil {
log.Errorf("failed to parse token expiration: %v, using default value: %d minutes", err, expiration)
return
}
if i <= 0 {
log.Warningf("invalid token expiration, using default value: %d minutes", expiration)
return
}
expiration = i
}
log.Infof("token expiration: %d minutes", expiration) log.Infof("token expiration: %d minutes", expiration)
} }
@ -109,7 +91,7 @@ func FilterAccess(username string, a *token.ResourceActions) {
repoLength := len(repoSplit) repoLength := len(repoSplit)
if repoLength > 1 { //Only check the permission when the requested image has a namespace, i.e. project if repoLength > 1 { //Only check the permission when the requested image has a namespace, i.e. project
var projectName string var projectName string
registryURL := os.Getenv("HARBOR_REG_URL") registryURL := config.ExtRegistryURL()
if repoSplit[0] == registryURL { if repoSplit[0] == registryURL {
projectName = repoSplit[1] projectName = repoSplit[1]
log.Infof("Detected Registry URL in Project Name. Assuming this is a notary request and setting Project Name as %s\n", projectName) log.Infof("Detected Registry URL in Project Name. Assuming this is a notary request and setting Project Name as %s\n", projectName)

View File

@ -18,14 +18,14 @@ package utils
import ( import (
"net/http" "net/http"
"os"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/config"
) )
// VerifySecret verifies the UI_SECRET cookie in a http request. // VerifySecret verifies the UI_SECRET cookie in a http request.
func VerifySecret(r *http.Request) bool { func VerifySecret(r *http.Request) bool {
secret := os.Getenv("UI_SECRET") secret := config.UISecret()
c, err := r.Cookie("uisecret") c, err := r.Cookie("uisecret")
if err != nil { if err != nil {
log.Warningf("Failed to get secret cookie, error: %v", err) log.Warningf("Failed to get secret cookie, error: %v", err)