Move Settings of HTTP auth proxy (#7047)

Previously the settings of HTTP authproxy were set in environment
variable.
This commit move them to the configuration API

Signed-off-by: Daniel Jiang <jiangd@vmware.com>
This commit is contained in:
Daniel Jiang 2019-03-01 14:11:14 +08:00 committed by Yan
parent f79895b6d8
commit 321874c815
9 changed files with 162 additions and 77 deletions

View File

@ -30,7 +30,7 @@ type Item struct {
Name string `json:"name,omitempty"`
// It can be &IntType{}, &StringType{}, &BoolType{}, &PasswordType{}, &MapType{} etc, any type interface implementation
ItemType Type
// Is this settign can be modified after configure
// TODO: Clarify the usage of this attribute
Editable bool `json:"editable,omitempty"`
}
@ -44,6 +44,7 @@ const (
LdapGroupGroup = "ldapgroup"
EmailGroup = "email"
UAAGroup = "uaa"
HTTPAuthGroup = "http_auth"
DatabaseGroup = "database"
// Put all config items do not belong a existing group into basic
BasicGroup = "basic"
@ -57,6 +58,7 @@ var (
// 2. Get/Set config settings by CfgManager
// 3. CfgManager.Load()/CfgManager.Save() to load/save from configure storage.
ConfigList = []Item{
// TODO: All these Name should be reference to const, see #7040
{Name: "admin_initial_password", Scope: SystemScope, Group: BasicGroup, EnvKey: "HARBOR_ADMIN_PASSWORD", DefaultValue: "", ItemType: &PasswordType{}, Editable: true},
{Name: "admiral_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "ADMIRAL_URL", DefaultValue: "", ItemType: &StringType{}, Editable: false},
{Name: "auth_mode", Scope: UserScope, Group: BasicGroup, EnvKey: "AUTH_MODE", DefaultValue: "db_auth", ItemType: &AuthModeType{}, Editable: false},
@ -127,6 +129,10 @@ var (
{Name: "uaa_endpoint", Scope: UserScope, Group: UAAGroup, EnvKey: "UAA_ENDPOINT", DefaultValue: "", ItemType: &StringType{}, Editable: false},
{Name: "uaa_verify_cert", Scope: UserScope, Group: UAAGroup, EnvKey: "UAA_VERIFY_CERT", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
{Name: common.HTTPAuthProxyEndpoint, Scope: UserScope, Group: HTTPAuthGroup, EnvKey: "HTTP_AUTHPROXY_ENDPOINT", DefaultValue: "", ItemType: &StringType{}, Editable: false},
{Name: common.HTTPAuthProxySkipCertVerify, Scope: UserScope, Group: HTTPAuthGroup, EnvKey: "HTTP_AUTHPROXY_SKIP_CERT_VERIFY", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
{Name: common.HTTPAuthProxyAlwaysOnboard, Scope: UserScope, Group: HTTPAuthGroup, EnvKey: "HTTP_AUTHPROXY_ALWAYS_ONBOARD", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
{Name: "with_chartmuseum", Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_CHARTMUSEUM", DefaultValue: "false", ItemType: &BoolType{}, Editable: true},
{Name: "with_clair", Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_CLAIR", DefaultValue: "false", ItemType: &BoolType{}, Editable: true},
{Name: "with_notary", Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_NOTARY", DefaultValue: "false", ItemType: &BoolType{}, Editable: true},

View File

@ -61,11 +61,11 @@ type AuthModeType struct {
}
func (t *AuthModeType) validate(str string) error {
if str == common.LDAPAuth || str == common.DBAuth || str == common.UAAAuth {
if str == common.LDAPAuth || str == common.DBAuth || str == common.UAAAuth || str == common.HTTPAuth {
return nil
}
return fmt.Errorf("invalid %s, shoud be one of %s, %s, %s",
common.AUTHMode, common.DBAuth, common.LDAPAuth, common.UAAAuth)
return fmt.Errorf("invalid %s, shoud be one of %s, %s, %s, %s",
common.AUTHMode, common.DBAuth, common.LDAPAuth, common.UAAAuth, common.HTTPAuth)
}
// ProjectCreationRestrictionType ...

View File

@ -95,6 +95,10 @@ const (
UAAClientID = "uaa_client_id"
UAAClientSecret = "uaa_client_secret"
UAAVerifyCert = "uaa_verify_cert"
HTTPAuthProxyEndpoint = "http_authproxy_endpoint"
HTTPAuthProxySkipCertVerify = "http_authproxy_skip_cert_verify"
HTTPAuthProxyAlwaysOnboard = "http_authproxy_always_onboard"
DefaultClairEndpoint = "http://clair:6060"
CfgDriverDB = "db"
NewHarborAdminName = "admin@harbor.local"
@ -107,7 +111,6 @@ const (
DefaultCoreEndpoint = "http://core:8080"
DefaultNotaryEndpoint = "http://notary-server:4443"
LdapGroupType = 1
ReloadKey = "reload_key"
LdapGroupAdminDn = "ldap_group_admin_dn"
DefaultRegistryControllerEndpoint = "http://registryctl:8080"
WithChartMuseum = "with_chartmuseum"

View File

@ -133,3 +133,12 @@ func ArrayEqual(arrayA, arrayB []int) bool {
}
return true
}
// ClearHTTPAuthProxyUsers remove the records from harbor_users to delete all user imported via
// HTTP Auth Proxy
func ClearHTTPAuthProxyUsers() error {
o := GetOrmer()
sql := "DELETE FROM harbor_user WHERE comment='By Authproxy'"
_, err := o.Raw(sql).Exec()
return err
}

View File

@ -65,6 +65,13 @@ type Email struct {
Insecure bool `json:"insecure"`
}
// HTTPAuthProxy wraps the settings for HTTP auth proxy
type HTTPAuthProxy struct {
Endpoint string `json:"endpoint"`
SkipCertVerify bool `json:"skip_cert_verify"`
AlwaysOnBoard bool `json:"always_onboard"`
}
/*
// Registry ...
type Registry struct {

View File

@ -22,13 +22,17 @@ import (
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/auth"
"github.com/goharbor/harbor/src/core/config"
"io/ioutil"
"net/http"
"os"
"strings"
"sync"
"time"
)
const refreshDuration = 5 * time.Second
const userEntryComment = "By Authproxy"
// Auth implements HTTP authenticator the required attributes.
// The attribute Endpoint is the HTTP endpoint to which the POST request should be issued for authentication
type Auth struct {
@ -37,12 +41,19 @@ type Auth struct {
Endpoint string
SkipCertVerify bool
AlwaysOnboard bool
settingTimeStamp time.Time
client *http.Client
}
// Authenticate issues http POST request to Endpoint if it returns 200 the authentication is considered success.
func (a *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
a.ensure()
err := a.ensure()
if err != nil {
if a.Endpoint == "" {
return nil, fmt.Errorf("failed to initialize HTTP Auth Proxy Authenticator, error: %v", err)
}
log.Warningf("Failed to refresh configuration for HTTP Auth Proxy Authenticator, error: %v, old settings will be used", err)
}
req, err := http.NewRequest(http.MethodPost, a.Endpoint, nil)
if err != nil {
return nil, fmt.Errorf("failed to send request, error: %v", err)
@ -109,7 +120,7 @@ func (a *Auth) fillInModel(u *models.User) error {
}
u.Realname = u.Username
u.Password = "1234567ab"
u.Comment = "By Authproxy"
u.Comment = userEntryComment
if strings.Contains(u.Username, "@") {
u.Email = u.Username
} else {
@ -118,13 +129,17 @@ func (a *Auth) fillInModel(u *models.User) error {
return nil
}
func (a *Auth) ensure() {
func (a *Auth) ensure() error {
a.Lock()
defer a.Unlock()
if a.Endpoint == "" {
a.Endpoint = os.Getenv("AUTHPROXY_ENDPOINT")
a.SkipCertVerify = strings.EqualFold(os.Getenv("AUTHPROXY_SKIP_CERT_VERIFY"), "true")
a.AlwaysOnboard = strings.EqualFold(os.Getenv("AUTHPROXY_ALWAYS_ONBOARD"), "true")
if time.Now().Sub(a.settingTimeStamp) >= refreshDuration {
setting, err := config.HTTPAuthProxySetting()
if err != nil {
return err
}
a.Endpoint = setting.Endpoint
a.SkipCertVerify = setting.SkipCertVerify
a.AlwaysOnboard = setting.AlwaysOnBoard
}
if a.client == nil {
tr := &http.Transport{
@ -136,6 +151,7 @@ func (a *Auth) ensure() {
Transport: tr,
}
}
return nil
}
func init() {

View File

@ -15,13 +15,16 @@
package authproxy
import (
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/models"
cut "github.com/goharbor/harbor/src/common/utils/test"
"github.com/goharbor/harbor/src/core/auth"
"github.com/goharbor/harbor/src/core/auth/authproxy/test"
"github.com/stretchr/testify/assert"
"net/http/httptest"
"os"
"testing"
"time"
)
var mockSvr *httptest.Server
@ -30,13 +33,22 @@ var pwd = "1234567ab"
var cmt = "By Authproxy"
func TestMain(m *testing.M) {
cut.InitDatabaseFromEnv()
if err := dao.ClearHTTPAuthProxyUsers(); err != nil {
panic(err)
}
mockSvr = test.NewMockServer(map[string]string{"jt": "pp", "Admin@vsphere.local": "Admin!23"})
defer mockSvr.Close()
a = &Auth{
Endpoint: mockSvr.URL + "/test/login",
SkipCertVerify: true,
// So it won't require mocking the cfgManager
settingTimeStamp: time.Now(),
}
rc := m.Run()
if err := dao.ClearHTTPAuthProxyUsers(); err != nil {
panic(err)
}
if rc != 0 {
os.Exit(rc)
}
@ -104,7 +116,6 @@ func TestAuth_Authenticate(t *testing.T) {
}
}
/* TODO: Enable this case after adminserver refactor is merged.
func TestAuth_PostAuthenticate(t *testing.T) {
type tc struct {
input *models.User
@ -120,7 +131,7 @@ func TestAuth_PostAuthenticate(t *testing.T) {
Email: "jt@placeholder.com",
Realname: "jt",
Password: pwd,
Comment: fmt.Sprintf(cmtTmpl, mockSvr.URL+"/test/login"),
Comment: userEntryComment,
},
},
{
@ -129,16 +140,19 @@ func TestAuth_PostAuthenticate(t *testing.T) {
},
expect: models.User{
Username: "Admin@vsphere.local",
Email: "jt@placeholder.com",
Email: "Admin@vsphere.local",
Realname: "Admin@vsphere.local",
Password: pwd,
Comment: fmt.Sprintf(cmtTmpl, mockSvr.URL+"/test/login"),
Comment: userEntryComment,
},
},
}
for _, c := range suite {
a.PostAuthenticate(c.input)
assert.Equal(t, c.expect, *c.input)
assert.Equal(t, c.expect.Username, c.input.Username)
assert.Equal(t, c.expect.Email, c.input.Email)
assert.Equal(t, c.expect.Realname, c.input.Realname)
assert.Equal(t, c.expect.Comment, c.input.Comment)
}
}
*/

View File

@ -462,3 +462,17 @@ func GetClairHealthCheckServerURL() string {
}
return url
}
// HTTPAuthProxySetting returns the setting of HTTP Auth proxy. the settings are only meaningful when the auth_mode is
// set to http_auth
func HTTPAuthProxySetting() (*models.HTTPAuthProxy, error) {
if err := cfgMgr.Load(); err != nil {
return nil, err
}
return &models.HTTPAuthProxy{
Endpoint: cfgMgr.Get(common.HTTPAuthProxyEndpoint).GetString(),
SkipCertVerify: cfgMgr.Get(common.HTTPAuthProxySkipCertVerify).GetBool(),
AlwaysOnBoard: cfgMgr.Get(common.HTTPAuthProxyAlwaysOnboard).GetBool(),
}, nil
}

View File

@ -225,3 +225,19 @@ func TestConfigureValue_GetMap(t *testing.T) {
}
fmt.Printf("%+v\n", policy)
}
func TestHTTPAuthProxySetting(t *testing.T) {
m := map[string]interface{}{
common.HTTPAuthProxyAlwaysOnboard: "true",
common.HTTPAuthProxySkipCertVerify: "true",
common.HTTPAuthProxyEndpoint: "https://auth.proxy/suffix",
}
InitWithSettings(m)
v, e := HTTPAuthProxySetting()
assert.Nil(t, e)
assert.Equal(t, *v, models.HTTPAuthProxy{
Endpoint: "https://auth.proxy/suffix",
AlwaysOnBoard: true,
SkipCertVerify: true,
})
}