mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-20 15:48:26 +01:00
Merge pull request #9311 from airadier/autoonboard-and-custom-user-claim
Add options for automatic onboarding and username claim
This commit is contained in:
commit
d891e023db
@ -140,7 +140,9 @@ var (
|
|||||||
{Name: common.OIDCClientSecret, Scope: UserScope, Group: OIDCGroup, ItemType: &PasswordType{}},
|
{Name: common.OIDCClientSecret, Scope: UserScope, Group: OIDCGroup, ItemType: &PasswordType{}},
|
||||||
{Name: common.OIDCGroupsClaim, Scope: UserScope, Group: OIDCGroup, ItemType: &StringType{}},
|
{Name: common.OIDCGroupsClaim, Scope: UserScope, Group: OIDCGroup, ItemType: &StringType{}},
|
||||||
{Name: common.OIDCScope, Scope: UserScope, Group: OIDCGroup, ItemType: &StringType{}},
|
{Name: common.OIDCScope, Scope: UserScope, Group: OIDCGroup, ItemType: &StringType{}},
|
||||||
|
{Name: common.OIDCUserClaim, Scope: UserScope, Group: OIDCGroup, ItemType: &StringType{}},
|
||||||
{Name: common.OIDCVerifyCert, Scope: UserScope, Group: OIDCGroup, DefaultValue: "true", ItemType: &BoolType{}},
|
{Name: common.OIDCVerifyCert, Scope: UserScope, Group: OIDCGroup, DefaultValue: "true", ItemType: &BoolType{}},
|
||||||
|
{Name: common.OIDCAutoOnboard, Scope: UserScope, Group: OIDCGroup, DefaultValue: "false", ItemType: &BoolType{}},
|
||||||
|
|
||||||
{Name: common.WithChartMuseum, Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_CHARTMUSEUM", DefaultValue: "false", ItemType: &BoolType{}, Editable: true},
|
{Name: common.WithChartMuseum, Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_CHARTMUSEUM", DefaultValue: "false", ItemType: &BoolType{}, Editable: true},
|
||||||
{Name: common.WithClair, Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_CLAIR", DefaultValue: "false", ItemType: &BoolType{}, Editable: true},
|
{Name: common.WithClair, Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_CLAIR", DefaultValue: "false", ItemType: &BoolType{}, Editable: true},
|
||||||
|
@ -106,7 +106,9 @@ const (
|
|||||||
OIDCClientSecret = "oidc_client_secret"
|
OIDCClientSecret = "oidc_client_secret"
|
||||||
OIDCVerifyCert = "oidc_verify_cert"
|
OIDCVerifyCert = "oidc_verify_cert"
|
||||||
OIDCGroupsClaim = "oidc_groups_claim"
|
OIDCGroupsClaim = "oidc_groups_claim"
|
||||||
|
OIDCAutoOnboard = "oidc_auto_onboard"
|
||||||
OIDCScope = "oidc_scope"
|
OIDCScope = "oidc_scope"
|
||||||
|
OIDCUserClaim = "oidc_user_claim"
|
||||||
|
|
||||||
CfgDriverDB = "db"
|
CfgDriverDB = "db"
|
||||||
NewHarborAdminName = "admin@harbor.local"
|
NewHarborAdminName = "admin@harbor.local"
|
||||||
|
@ -81,11 +81,13 @@ type OIDCSetting struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Endpoint string `json:"endpoint"`
|
Endpoint string `json:"endpoint"`
|
||||||
VerifyCert bool `json:"verify_cert"`
|
VerifyCert bool `json:"verify_cert"`
|
||||||
|
AutoOnboard bool `json:"auto_onboard"`
|
||||||
ClientID string `json:"client_id"`
|
ClientID string `json:"client_id"`
|
||||||
ClientSecret string `json:"client_secret"`
|
ClientSecret string `json:"client_secret"`
|
||||||
GroupsClaim string `json:"groups_claim"`
|
GroupsClaim string `json:"groups_claim"`
|
||||||
RedirectURL string `json:"redirect_url"`
|
RedirectURL string `json:"redirect_url"`
|
||||||
Scope []string `json:"scope"`
|
Scope []string `json:"scope"`
|
||||||
|
UserClaim string `json:"user_claim"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// QuotaSetting wraps the settings for Quota
|
// QuotaSetting wraps the settings for Quota
|
||||||
|
@ -19,16 +19,17 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
gooidc "github.com/coreos/go-oidc"
|
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
|
||||||
"golang.org/x/oauth2"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
gooidc "github.com/coreos/go-oidc"
|
||||||
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -294,7 +295,7 @@ func userInfoFromRemote(ctx context.Context, token *Token, setting models.OIDCSe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return userInfoFromClaims(u, setting.GroupsClaim)
|
return userInfoFromClaims(u, setting.GroupsClaim, setting.UserClaim)
|
||||||
}
|
}
|
||||||
|
|
||||||
func userInfoFromIDToken(ctx context.Context, token *Token, setting models.OIDCSetting) (*UserInfo, error) {
|
func userInfoFromIDToken(ctx context.Context, token *Token, setting models.OIDCSetting) (*UserInfo, error) {
|
||||||
@ -305,14 +306,28 @@ func userInfoFromIDToken(ctx context.Context, token *Token, setting models.OIDCS
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return userInfoFromClaims(idt, setting.GroupsClaim)
|
|
||||||
|
return userInfoFromClaims(idt, setting.GroupsClaim, setting.UserClaim)
|
||||||
}
|
}
|
||||||
|
|
||||||
func userInfoFromClaims(c claimsProvider, g string) (*UserInfo, error) {
|
func userInfoFromClaims(c claimsProvider, g, u string) (*UserInfo, error) {
|
||||||
res := &UserInfo{}
|
res := &UserInfo{}
|
||||||
if err := c.Claims(res); err != nil {
|
if err := c.Claims(res); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if u != "" {
|
||||||
|
allClaims := make(map[string]interface{})
|
||||||
|
if err := c.Claims(&allClaims); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
username, ok := allClaims[u].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("OIDC. Failed to recover Username from claim. Claim '%s' is invalid or not a string", u)
|
||||||
|
}
|
||||||
|
res.Username = username
|
||||||
|
|
||||||
|
}
|
||||||
res.Groups, res.hasGroupClaim = GroupsFromClaims(c, g)
|
res.Groups, res.hasGroupClaim = GroupsFromClaims(c, g)
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
@ -175,6 +175,7 @@ func TestUserInfoFromClaims(t *testing.T) {
|
|||||||
s := []struct {
|
s := []struct {
|
||||||
input map[string]interface{}
|
input map[string]interface{}
|
||||||
groupClaim string
|
groupClaim string
|
||||||
|
userClaim string
|
||||||
expect *UserInfo
|
expect *UserInfo
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@ -184,6 +185,7 @@ func TestUserInfoFromClaims(t *testing.T) {
|
|||||||
"groups": []interface{}{"g1", "g2"},
|
"groups": []interface{}{"g1", "g2"},
|
||||||
},
|
},
|
||||||
groupClaim: "grouplist",
|
groupClaim: "grouplist",
|
||||||
|
userClaim: "",
|
||||||
expect: &UserInfo{
|
expect: &UserInfo{
|
||||||
Issuer: "",
|
Issuer: "",
|
||||||
Subject: "",
|
Subject: "",
|
||||||
@ -200,6 +202,7 @@ func TestUserInfoFromClaims(t *testing.T) {
|
|||||||
"groups": []interface{}{"g1", "g2"},
|
"groups": []interface{}{"g1", "g2"},
|
||||||
},
|
},
|
||||||
groupClaim: "groups",
|
groupClaim: "groups",
|
||||||
|
userClaim: "",
|
||||||
expect: &UserInfo{
|
expect: &UserInfo{
|
||||||
Issuer: "",
|
Issuer: "",
|
||||||
Subject: "",
|
Subject: "",
|
||||||
@ -218,6 +221,7 @@ func TestUserInfoFromClaims(t *testing.T) {
|
|||||||
"groupclaim": []interface{}{},
|
"groupclaim": []interface{}{},
|
||||||
},
|
},
|
||||||
groupClaim: "groupclaim",
|
groupClaim: "groupclaim",
|
||||||
|
userClaim: "",
|
||||||
expect: &UserInfo{
|
expect: &UserInfo{
|
||||||
Issuer: "issuer",
|
Issuer: "issuer",
|
||||||
Subject: "subject000",
|
Subject: "subject000",
|
||||||
@ -227,9 +231,26 @@ func TestUserInfoFromClaims(t *testing.T) {
|
|||||||
hasGroupClaim: true,
|
hasGroupClaim: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"name": "Alvaro",
|
||||||
|
"email": "airadier@gmail.com",
|
||||||
|
"groups": []interface{}{"g1", "g2"},
|
||||||
|
},
|
||||||
|
groupClaim: "grouplist",
|
||||||
|
userClaim: "email",
|
||||||
|
expect: &UserInfo{
|
||||||
|
Issuer: "",
|
||||||
|
Subject: "",
|
||||||
|
Username: "airadier@gmail.com",
|
||||||
|
Email: "airadier@gmail.com",
|
||||||
|
Groups: []string{},
|
||||||
|
hasGroupClaim: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range s {
|
for _, tc := range s {
|
||||||
out, err := userInfoFromClaims(&fakeClaims{tc.input}, tc.groupClaim)
|
out, err := userInfoFromClaims(&fakeClaims{tc.input}, tc.groupClaim, tc.userClaim)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, *tc.expect, *out)
|
assert.Equal(t, *tc.expect, *out)
|
||||||
}
|
}
|
||||||
|
@ -440,11 +440,13 @@ func OIDCSetting() (*models.OIDCSetting, error) {
|
|||||||
Name: cfgMgr.Get(common.OIDCName).GetString(),
|
Name: cfgMgr.Get(common.OIDCName).GetString(),
|
||||||
Endpoint: cfgMgr.Get(common.OIDCEndpoint).GetString(),
|
Endpoint: cfgMgr.Get(common.OIDCEndpoint).GetString(),
|
||||||
VerifyCert: cfgMgr.Get(common.OIDCVerifyCert).GetBool(),
|
VerifyCert: cfgMgr.Get(common.OIDCVerifyCert).GetBool(),
|
||||||
|
AutoOnboard: cfgMgr.Get(common.OIDCAutoOnboard).GetBool(),
|
||||||
ClientID: cfgMgr.Get(common.OIDCCLientID).GetString(),
|
ClientID: cfgMgr.Get(common.OIDCCLientID).GetString(),
|
||||||
ClientSecret: cfgMgr.Get(common.OIDCClientSecret).GetString(),
|
ClientSecret: cfgMgr.Get(common.OIDCClientSecret).GetString(),
|
||||||
GroupsClaim: cfgMgr.Get(common.OIDCGroupsClaim).GetString(),
|
GroupsClaim: cfgMgr.Get(common.OIDCGroupsClaim).GetString(),
|
||||||
RedirectURL: extEndpoint + common.OIDCCallbackPath,
|
RedirectURL: extEndpoint + common.OIDCCallbackPath,
|
||||||
Scope: scope,
|
Scope: scope,
|
||||||
|
UserClaim: cfgMgr.Get(common.OIDCUserClaim).GetString(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,8 +253,10 @@ func TestOIDCSetting(t *testing.T) {
|
|||||||
common.OIDCName: "test",
|
common.OIDCName: "test",
|
||||||
common.OIDCEndpoint: "https://oidc.test",
|
common.OIDCEndpoint: "https://oidc.test",
|
||||||
common.OIDCVerifyCert: "true",
|
common.OIDCVerifyCert: "true",
|
||||||
|
common.OIDCAutoOnboard: "false",
|
||||||
common.OIDCScope: "openid, profile",
|
common.OIDCScope: "openid, profile",
|
||||||
common.OIDCGroupsClaim: "my_group",
|
common.OIDCGroupsClaim: "my_group",
|
||||||
|
common.OIDCUserClaim: "username",
|
||||||
common.OIDCCLientID: "client",
|
common.OIDCCLientID: "client",
|
||||||
common.OIDCClientSecret: "secret",
|
common.OIDCClientSecret: "secret",
|
||||||
common.ExtEndpoint: "https://harbor.test",
|
common.ExtEndpoint: "https://harbor.test",
|
||||||
@ -266,8 +268,10 @@ func TestOIDCSetting(t *testing.T) {
|
|||||||
assert.Equal(t, "https://oidc.test", v.Endpoint)
|
assert.Equal(t, "https://oidc.test", v.Endpoint)
|
||||||
assert.True(t, v.VerifyCert)
|
assert.True(t, v.VerifyCert)
|
||||||
assert.Equal(t, "my_group", v.GroupsClaim)
|
assert.Equal(t, "my_group", v.GroupsClaim)
|
||||||
|
assert.False(t, v.AutoOnboard)
|
||||||
assert.Equal(t, "client", v.ClientID)
|
assert.Equal(t, "client", v.ClientID)
|
||||||
assert.Equal(t, "secret", v.ClientSecret)
|
assert.Equal(t, "secret", v.ClientSecret)
|
||||||
assert.Equal(t, "https://harbor.test/c/oidc/callback", v.RedirectURL)
|
assert.Equal(t, "https://harbor.test/c/oidc/callback", v.RedirectURL)
|
||||||
assert.ElementsMatch(t, []string{"openid", "profile"}, v.Scope)
|
assert.ElementsMatch(t, []string{"openid", "profile"}, v.Scope)
|
||||||
|
assert.Equal(t, "username", v.UserClaim)
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,11 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/goharbor/harbor/src/common/dao/group"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/goharbor/harbor/src/common/dao/group"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
@ -122,11 +123,37 @@ func (oc *OIDCController) Callback() {
|
|||||||
}
|
}
|
||||||
oc.SetSession(tokenKey, tokenBytes)
|
oc.SetSession(tokenKey, tokenBytes)
|
||||||
|
|
||||||
|
oidcSettings, err := config.OIDCSetting()
|
||||||
|
if err != nil {
|
||||||
|
oc.SendInternalServerError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if u == nil {
|
if u == nil {
|
||||||
oc.SetSession(userInfoKey, string(ouDataStr))
|
// Recover the username from d.Username by default
|
||||||
oc.Controller.Redirect(fmt.Sprintf("/oidc-onboard?username=%s", strings.Replace(info.Username, " ", "_", -1)),
|
username := info.Username
|
||||||
http.StatusFound)
|
|
||||||
|
// Fix blanks in username
|
||||||
|
username = strings.Replace(username, " ", "_", -1)
|
||||||
|
|
||||||
|
// If automatic onboard is enabled, skip the onboard page
|
||||||
|
if oidcSettings.AutoOnboard {
|
||||||
|
log.Debug("Doing automatic onboarding\n")
|
||||||
|
user, onboarded := userOnboard(oc, info, username, tokenBytes)
|
||||||
|
if onboarded == false {
|
||||||
|
log.Error("User not onboarded\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debug("User automatically onboarded\n")
|
||||||
|
u = user
|
||||||
} else {
|
} else {
|
||||||
|
oc.SetSession(userInfoKey, string(ouDataStr))
|
||||||
|
oc.Controller.Redirect(fmt.Sprintf("/oidc-onboard?username=%s", username), http.StatusFound)
|
||||||
|
// Once redirected, no further actions are done
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gids, err := group.PopulateGroup(models.UserGroupsFromName(info.Groups, common.OIDCGroupType))
|
gids, err := group.PopulateGroup(models.UserGroupsFromName(info.Groups, common.OIDCGroupType))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("Failed to populate groups, error: %v, user will have empty group list, username: %s", err, info.Username)
|
log.Warningf("Failed to populate groups, error: %v, user will have empty group list, username: %s", err, info.Username)
|
||||||
@ -145,7 +172,50 @@ func (oc *OIDCController) Callback() {
|
|||||||
}
|
}
|
||||||
oc.PopulateUserSession(*u)
|
oc.PopulateUserSession(*u)
|
||||||
oc.Controller.Redirect("/", http.StatusFound)
|
oc.Controller.Redirect("/", http.StatusFound)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func userOnboard(oc *OIDCController, info *oidc.UserInfo, username string, tokenBytes []byte) (*models.User, bool) {
|
||||||
|
s, t, err := secretAndToken(tokenBytes)
|
||||||
|
if err != nil {
|
||||||
|
oc.SendInternalServerError(err)
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gids, err := group.PopulateGroup(models.UserGroupsFromName(info.Groups, common.OIDCGroupType))
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("Failed to populate group user will have empty group list. username: %s", username)
|
||||||
|
}
|
||||||
|
|
||||||
|
oidcUser := models.OIDCUser{
|
||||||
|
SubIss: info.Subject + info.Issuer,
|
||||||
|
Secret: s,
|
||||||
|
Token: t,
|
||||||
|
}
|
||||||
|
|
||||||
|
user := models.User{
|
||||||
|
Username: username,
|
||||||
|
Realname: username,
|
||||||
|
Email: info.Email,
|
||||||
|
GroupIDs: gids,
|
||||||
|
OIDCUserMeta: &oidcUser,
|
||||||
|
Comment: oidcUserComment,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("User created: %+v\n", user)
|
||||||
|
|
||||||
|
err = dao.OnBoardOIDCUser(&user)
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), dao.ErrDupUser.Error()) {
|
||||||
|
oc.RenderError(http.StatusConflict, "Conflict, the user with same username or email has been onboarded.")
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
oc.SendInternalServerError(err)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return &user, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Onboard handles the request to onboard a user authenticated via OIDC provider
|
// Onboard handles the request to onboard a user authenticated via OIDC provider
|
||||||
@ -176,51 +246,20 @@ func (oc *OIDCController) Onboard() {
|
|||||||
oc.SendBadRequestError(errors.New("Failed to get OIDC token from session"))
|
oc.SendBadRequestError(errors.New("Failed to get OIDC token from session"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s, t, err := secretAndToken(tb)
|
|
||||||
if err != nil {
|
|
||||||
oc.SendInternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
d := &oidc.UserInfo{}
|
d := &oidc.UserInfo{}
|
||||||
err = json.Unmarshal([]byte(userInfoStr), &d)
|
err := json.Unmarshal([]byte(userInfoStr), &d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
oc.SendInternalServerError(err)
|
oc.SendInternalServerError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
gids, err := group.PopulateGroup(models.UserGroupsFromName(d.Groups, common.OIDCGroupType))
|
|
||||||
if err != nil {
|
|
||||||
log.Warningf("Failed to populate group user will have empty group list. username: %s", username)
|
|
||||||
}
|
|
||||||
oidcUser := models.OIDCUser{
|
|
||||||
SubIss: d.Subject + d.Issuer,
|
|
||||||
Secret: s,
|
|
||||||
Token: t,
|
|
||||||
}
|
|
||||||
|
|
||||||
email := d.Email
|
|
||||||
user := models.User{
|
|
||||||
Username: username,
|
|
||||||
Realname: d.Username,
|
|
||||||
Email: email,
|
|
||||||
GroupIDs: gids,
|
|
||||||
OIDCUserMeta: &oidcUser,
|
|
||||||
Comment: oidcUserComment,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = dao.OnBoardOIDCUser(&user)
|
|
||||||
if err != nil {
|
|
||||||
if strings.Contains(err.Error(), dao.ErrDupUser.Error()) {
|
|
||||||
oc.RenderError(http.StatusConflict, "Conflict, the user with same username or email has been onboarded.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
oc.SendInternalServerError(err)
|
|
||||||
oc.DelSession(userInfoKey)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if user, onboarded := userOnboard(oc, d, username, tb); onboarded {
|
||||||
user.OIDCUserMeta = nil
|
user.OIDCUserMeta = nil
|
||||||
oc.DelSession(userInfoKey)
|
oc.DelSession(userInfoKey)
|
||||||
oc.PopulateUserSession(user)
|
oc.PopulateUserSession(*user)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func secretAndToken(tokenBytes []byte) (string, string, error) {
|
func secretAndToken(tokenBytes []byte) (string, string, error) {
|
||||||
|
@ -390,8 +390,37 @@
|
|||||||
[(ngModel)]="currentConfig.oidc_verify_cert.value" />
|
[(ngModel)]="currentConfig.oidc_verify_cert.value" />
|
||||||
</clr-checkbox-wrapper>
|
</clr-checkbox-wrapper>
|
||||||
</clr-checkbox-container>
|
</clr-checkbox-container>
|
||||||
|
<clr-checkbox-container>
|
||||||
|
<label for="oidcAutoOnboard">{{'CONFIG.OIDC.OIDC_AUTOONBOARD' | translate}}
|
||||||
|
<clr-tooltip>
|
||||||
|
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
|
||||||
|
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
|
||||||
|
<span>{{'TOOLTIP.OIDC_AUTOONBOARD' | translate}}</span>
|
||||||
|
</clr-tooltip-content>
|
||||||
|
</clr-tooltip>
|
||||||
|
</label>
|
||||||
|
<clr-checkbox-wrapper>
|
||||||
|
<input type="checkbox" clrCheckbox name="oidcAutoOnboard" id="oidcAutoOnboard"
|
||||||
|
[disabled]="disabled(currentConfig.oidc_auto_onboard)"
|
||||||
|
[(ngModel)]="currentConfig.oidc_auto_onboard.value" />
|
||||||
|
</clr-checkbox-wrapper>
|
||||||
|
</clr-checkbox-container>
|
||||||
|
<clr-input-container>
|
||||||
|
<label for="oidcUserClaim">{{'CONFIG.OIDC.USER_CLAIM' | translate}}
|
||||||
|
<clr-tooltip>
|
||||||
|
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
|
||||||
|
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
|
||||||
|
<span>{{'TOOLTIP.OIDC_USER_CLAIM' | translate}}</span>
|
||||||
|
</clr-tooltip-content>
|
||||||
|
</clr-tooltip>
|
||||||
|
</label>
|
||||||
|
<input clrInput name="oidcUserClaim" type="text" #oidcUserClaimInput="ngModel"
|
||||||
|
[(ngModel)]="currentConfig.oidc_user_claim.value" id="oidcUserClaim" size="40"
|
||||||
|
[disabled]="disabled(currentConfig.oidc_user_claim)" pattern="^[a-zA-Z0-9_-]*$">
|
||||||
|
</clr-input-container>
|
||||||
<div class="oidc-tip">{{ 'CONFIG.OIDC.OIDC_REDIREC_URL' | translate}}
|
<div class="oidc-tip">{{ 'CONFIG.OIDC.OIDC_REDIREC_URL' | translate}}
|
||||||
<span>{{redirectUrl}}/c/oidc/callback</span></div>
|
<span>{{redirectUrl}}/c/oidc/callback</span>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</form>
|
</form>
|
||||||
<div>
|
<div>
|
||||||
|
@ -102,6 +102,8 @@
|
|||||||
"OIDC_VERIFYCERT": "Uncheck this box if your OIDC server is hosted via self-signed certificate.",
|
"OIDC_VERIFYCERT": "Uncheck this box if your OIDC server is hosted via self-signed certificate.",
|
||||||
"OIDC_GROUP_CLAIM": "The name of Claim in the ID token whose value is the list of group names.",
|
"OIDC_GROUP_CLAIM": "The name of Claim in the ID token whose value is the list of group names.",
|
||||||
"OIDC_GROUP_CLAIM_WARNING": "It can only contain letters, numbers, underscores, and the input length is no more than 256 characters.",
|
"OIDC_GROUP_CLAIM_WARNING": "It can only contain letters, numbers, underscores, and the input length is no more than 256 characters.",
|
||||||
|
"OIDC_AUTOONBOARD": "Skip the onboarding screen, so user cannot change its username. Username is provided from ID Token",
|
||||||
|
"OIDC_USER_CLAIM": "The name of the claim in the ID Token where the username is retrieved from. If not specified, it will default to 'name'",
|
||||||
"NEW_SECRET": "The secret must longer than 8 chars with at least 1 uppercase letter, 1 lowercase letter and 1 number"
|
"NEW_SECRET": "The secret must longer than 8 chars with at least 1 uppercase letter, 1 lowercase letter and 1 number"
|
||||||
},
|
},
|
||||||
"PLACEHOLDER": {
|
"PLACEHOLDER": {
|
||||||
@ -911,6 +913,8 @@
|
|||||||
"CLIENTSECRET": "OIDC Client Secret",
|
"CLIENTSECRET": "OIDC Client Secret",
|
||||||
"SCOPE": "OIDC Scope",
|
"SCOPE": "OIDC Scope",
|
||||||
"OIDC_VERIFYCERT": "Verify Certificate",
|
"OIDC_VERIFYCERT": "Verify Certificate",
|
||||||
|
"OIDC_AUTOONBOARD": "Automatic onboarding",
|
||||||
|
"USER_CLAIM": "Username Claim",
|
||||||
"OIDC_SETNAME": "Set OIDC Username",
|
"OIDC_SETNAME": "Set OIDC Username",
|
||||||
"OIDC_SETNAMECONTENT": "You must create a Harbor username the first time when authenticating via a third party(OIDC).This will be used within Harbor to be associated with projects, roles, etc.",
|
"OIDC_SETNAMECONTENT": "You must create a Harbor username the first time when authenticating via a third party(OIDC).This will be used within Harbor to be associated with projects, roles, etc.",
|
||||||
"OIDC_USERNAME": "Username",
|
"OIDC_USERNAME": "Username",
|
||||||
|
@ -98,7 +98,9 @@ export class Configuration {
|
|||||||
oidc_client_id?: StringValueItem;
|
oidc_client_id?: StringValueItem;
|
||||||
oidc_client_secret?: StringValueItem;
|
oidc_client_secret?: StringValueItem;
|
||||||
oidc_verify_cert?: BoolValueItem;
|
oidc_verify_cert?: BoolValueItem;
|
||||||
|
oidc_auto_onboard?: BoolValueItem;
|
||||||
oidc_scope?: StringValueItem;
|
oidc_scope?: StringValueItem;
|
||||||
|
oidc_user_claim?: StringValueItem;
|
||||||
count_per_project: NumberValueItem;
|
count_per_project: NumberValueItem;
|
||||||
storage_per_project: NumberValueItem;
|
storage_per_project: NumberValueItem;
|
||||||
cfg_expiration: NumberValueItem;
|
cfg_expiration: NumberValueItem;
|
||||||
@ -155,8 +157,10 @@ export class Configuration {
|
|||||||
this.oidc_client_id = new StringValueItem('', true);
|
this.oidc_client_id = new StringValueItem('', true);
|
||||||
this.oidc_client_secret = new StringValueItem('', true);
|
this.oidc_client_secret = new StringValueItem('', true);
|
||||||
this.oidc_verify_cert = new BoolValueItem(false, true);
|
this.oidc_verify_cert = new BoolValueItem(false, true);
|
||||||
|
this.oidc_auto_onboard = new BoolValueItem(false, true);
|
||||||
this.oidc_scope = new StringValueItem('', true);
|
this.oidc_scope = new StringValueItem('', true);
|
||||||
this.oidc_groups_claim = new StringValueItem('', true);
|
this.oidc_groups_claim = new StringValueItem('', true);
|
||||||
|
this.oidc_user_claim = new StringValueItem('', true);
|
||||||
this.count_per_project = new NumberValueItem(-1, true);
|
this.count_per_project = new NumberValueItem(-1, true);
|
||||||
this.storage_per_project = new NumberValueItem(-1, true);
|
this.storage_per_project = new NumberValueItem(-1, true);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user