update per comments and fix govet error

Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
wang yan 2019-10-21 23:15:51 +08:00
parent 22b4ea0f89
commit 5996189bb0
31 changed files with 229 additions and 155 deletions

View File

@ -28,7 +28,8 @@ const (
ActionDelete = Action("delete")
ActionList = Action("list")
ActionOperate = Action("operate")
ActionOperate = Action("operate")
ActionScannerPull = Action("scannerpull") // for robot account created by scanner to pull image, bypass the policy check
)
// const resource variables

View File

@ -64,11 +64,6 @@ func (s *SecurityContext) IsSysAdmin() bool {
return s.ctx.IsSysAdmin()
}
// PolicyCheck ...
func (s *SecurityContext) PolicyCheck() bool {
return true
}
// IsSolutionUser ...
func (s *SecurityContext) IsSolutionUser() bool {
return false

View File

@ -27,8 +27,6 @@ type Context interface {
GetUsername() string
// IsSysAdmin returns whether the user is system admin
IsSysAdmin() bool
// PolicyCheck returns whether the middlerwares apply to the request
PolicyCheck() bool
// IsSolutionUser returns whether the user is solution user
IsSolutionUser() bool
// Get current user's all project

View File

@ -61,11 +61,6 @@ func (s *SecurityContext) IsSysAdmin() bool {
return s.user.HasAdminRole
}
// PolicyCheck ...
func (s *SecurityContext) PolicyCheck() bool {
return true
}
// IsSolutionUser ...
func (s *SecurityContext) IsSolutionUser() bool {
return false

View File

@ -23,19 +23,17 @@ import (
// SecurityContext implements security.Context interface based on database
type SecurityContext struct {
robot *model.Robot
pm promgr.ProjectManager
policy []*rbac.Policy
polichCheck bool
robot *model.Robot
pm promgr.ProjectManager
policy []*rbac.Policy
}
// NewSecurityContext ...
func NewSecurityContext(robot *model.Robot, pm promgr.ProjectManager, policy []*rbac.Policy, polichCheck bool) *SecurityContext {
func NewSecurityContext(robot *model.Robot, pm promgr.ProjectManager, policy []*rbac.Policy) *SecurityContext {
return &SecurityContext{
robot: robot,
pm: pm,
policy: policy,
polichCheck: polichCheck,
robot: robot,
pm: pm,
policy: policy,
}
}
@ -58,11 +56,6 @@ func (s *SecurityContext) IsSysAdmin() bool {
return false
}
// PolicyCheck ...
func (s *SecurityContext) PolicyCheck() bool {
return s.polichCheck
}
// IsSolutionUser robot cannot be a system admin
func (s *SecurityContext) IsSolutionUser() bool {
return false

View File

@ -93,45 +93,45 @@ func TestMain(m *testing.M) {
func TestIsAuthenticated(t *testing.T) {
// unauthenticated
ctx := NewSecurityContext(nil, nil, nil, true)
ctx := NewSecurityContext(nil, nil, nil)
assert.False(t, ctx.IsAuthenticated())
// authenticated
ctx = NewSecurityContext(&model.Robot{
Name: "test",
Disabled: false,
}, nil, nil, true)
}, nil, nil)
assert.True(t, ctx.IsAuthenticated())
}
func TestGetUsername(t *testing.T) {
// unauthenticated
ctx := NewSecurityContext(nil, nil, nil, true)
ctx := NewSecurityContext(nil, nil, nil)
assert.Equal(t, "", ctx.GetUsername())
// authenticated
ctx = NewSecurityContext(&model.Robot{
Name: "test",
Disabled: false,
}, nil, nil, true)
}, nil, nil)
assert.Equal(t, "test", ctx.GetUsername())
}
func TestIsSysAdmin(t *testing.T) {
// unauthenticated
ctx := NewSecurityContext(nil, nil, nil, true)
ctx := NewSecurityContext(nil, nil, nil)
assert.False(t, ctx.IsSysAdmin())
// authenticated, non admin
ctx = NewSecurityContext(&model.Robot{
Name: "test",
Disabled: false,
}, nil, nil, true)
}, nil, nil)
assert.False(t, ctx.IsSysAdmin())
}
func TestIsSolutionUser(t *testing.T) {
ctx := NewSecurityContext(nil, nil, nil, true)
ctx := NewSecurityContext(nil, nil, nil)
assert.False(t, ctx.IsSolutionUser())
}
@ -147,7 +147,7 @@ func TestHasPullPerm(t *testing.T) {
Description: "desc",
}
ctx := NewSecurityContext(robot, pm, policies, true)
ctx := NewSecurityContext(robot, pm, policies)
resource := rbac.NewProjectNamespace(private.ProjectID).Resource(rbac.ResourceRepository)
assert.True(t, ctx.Can(rbac.ActionPull, resource))
}
@ -164,7 +164,7 @@ func TestHasPushPerm(t *testing.T) {
Description: "desc",
}
ctx := NewSecurityContext(robot, pm, policies, true)
ctx := NewSecurityContext(robot, pm, policies)
resource := rbac.NewProjectNamespace(private.ProjectID).Resource(rbac.ResourceRepository)
assert.True(t, ctx.Can(rbac.ActionPush, resource))
}
@ -185,20 +185,20 @@ func TestHasPushPullPerm(t *testing.T) {
Description: "desc",
}
ctx := NewSecurityContext(robot, pm, policies, true)
ctx := NewSecurityContext(robot, pm, policies)
resource := rbac.NewProjectNamespace(private.ProjectID).Resource(rbac.ResourceRepository)
assert.True(t, ctx.Can(rbac.ActionPush, resource) && ctx.Can(rbac.ActionPull, resource))
}
func TestGetMyProjects(t *testing.T) {
ctx := NewSecurityContext(nil, nil, nil, true)
ctx := NewSecurityContext(nil, nil, nil)
projects, err := ctx.GetMyProjects()
require.Nil(t, err)
assert.Nil(t, projects)
}
func TestGetProjectRoles(t *testing.T) {
ctx := NewSecurityContext(nil, nil, nil, true)
ctx := NewSecurityContext(nil, nil, nil)
roles := ctx.GetProjectRoles("test")
assert.Nil(t, roles)
}

View File

@ -66,11 +66,6 @@ func (s *SecurityContext) IsSysAdmin() bool {
return false
}
// PolicyCheck ...
func (s *SecurityContext) PolicyCheck() bool {
return true
}
// IsSolutionUser ...
func (s *SecurityContext) IsSolutionUser() bool {
return s.IsAuthenticated()

View File

@ -76,7 +76,7 @@ func GetTargets(notaryEndpoint string, username string, fqRepo string) ([]model.
Type: "repository",
Name: fqRepo,
Actions: []string{"pull"},
}}, true)
}})
if err != nil {
return nil, err
}

View File

@ -208,7 +208,7 @@ func RefreshToken(ctx context.Context, token *Token) (*Token, error) {
return &Token{Token: *t, IDToken: it}, nil
}
// GroupsFromToken returns the list of group name in the token, the claim of the group list is set in OIDCSetting.
// GroupsFromToken returns the list of group name in the token, the claims of the group list is set in OIDCSetting.
// It's designed not to return errors, in case of unexpected situation it will log and return empty list.
func GroupsFromToken(token *gooidc.IDToken) []string {
if token == nil {
@ -217,7 +217,7 @@ func GroupsFromToken(token *gooidc.IDToken) []string {
}
setting := provider.setting.Load().(models.OIDCSetting)
if len(setting.GroupsClaim) == 0 {
log.Warning("Group claim is not set in OIDC setting returning empty group list.")
log.Warning("Group claims is not set in OIDC setting returning empty group list.")
return []string{}
}
var c map[string]interface{}
@ -233,7 +233,7 @@ func groupsFromClaim(claimMap map[string]interface{}, k string) []string {
var res []string
g, ok := claimMap[k].([]interface{})
if !ok {
log.Warningf("Unable to get groups from claims, claims: %+v, groups claim key: %s", claimMap, k)
log.Warningf("Unable to get groups from claims, claims: %+v, groups claims key: %s", claimMap, k)
return res
}
for _, e := range g {

View File

@ -346,7 +346,7 @@ type rawTokenGenerator struct {
// generate token directly
func (r *rawTokenGenerator) generate(scopes []*token.ResourceActions, endpoint string) (*models.Token, error) {
return token_util.MakeToken(r.username, r.service, scopes, true)
return token_util.MakeToken(r.username, r.service, scopes)
}
func buildPingURL(endpoint string) string {

View File

@ -108,7 +108,7 @@ func (r *RobotAPI) Post() {
return
}
robotReq.Visible = true
robotReq.PolicyCheck = true
robotReq.ByPassPolicyCheck = false
robotReq.ProjectID = r.project.ProjectID
if err := validateRobotReq(r.project, &robotReq); err != nil {

View File

@ -44,7 +44,7 @@ import (
"github.com/goharbor/harbor/src/pkg/authproxy"
"github.com/goharbor/harbor/src/pkg/robot"
pkg_token "github.com/goharbor/harbor/src/pkg/token"
"github.com/goharbor/harbor/src/pkg/token/claim"
robot_claim "github.com/goharbor/harbor/src/pkg/token/claims/robot"
)
// ContextValueKey for content value
@ -189,17 +189,16 @@ func (r *robotAuthReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
if !strings.HasPrefix(robotName, common.RobotPrefix) {
return false
}
rClaims := &claim.Robot{}
rClaims := &robot_claim.Claim{}
opt := pkg_token.DefaultTokenOptions()
rtk, err := pkg_token.Parse(opt, robotTk, rClaims)
if err != nil {
log.Errorf("failed to decrypt robot token, %v", err)
return false
}
// Do authn for robot account, as Harbor only stores the token ID, just validate the ID and disable.
ctr := robot.RobotCtr
robot, err := ctr.GetRobotAccount(rtk.Claims.(*claim.Robot).TokenID)
robot, err := ctr.GetRobotAccount(rtk.Claims.(*robot_claim.Claim).TokenID)
if err != nil {
log.Errorf("failed to get robot %s: %v", robotName, err)
return false
@ -218,7 +217,7 @@ func (r *robotAuthReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
}
log.Debug("creating robot account security context...")
pm := config.GlobalProjectMgr
securCtx := robotCtx.NewSecurityContext(robot, pm, rtk.Claims.(*claim.Robot).Access, rClaims.PolicyCheck)
securCtx := robotCtx.NewSecurityContext(robot, pm, rtk.Claims.(*robot_claim.Claim).Access)
setSecurCtxAndPM(ctx.Request, securCtx, pm)
return true
}

View File

@ -26,7 +26,7 @@ const (
SIZEQUOTA = "sizequota"
COUNTQUOTA = "countquota"
IMMUTABLE = "immutable"
REGTOKEN = "REGTOKEN"
REGTOKEN = "regtoken"
)
// ChartMiddlewares middlewares for chart server

View File

@ -50,10 +50,12 @@ func (cth contentTrustHandler) ServeHTTP(rw http.ResponseWriter, req *http.Reque
return
}
// Token bypass policy check
policyCheck := req.Context().Value(util.PolicyCheckCtxKey)
if policyCheck != nil && !policyCheck.(bool) {
cth.next.ServeHTTP(rw, req)
return
if bypassPC := req.Context().Value(util.ByPassPolicyCheckCtxKey); bypassPC != nil {
bypassPolicyCheck, ok := bypassPC.(bool)
if ok && bypassPolicyCheck {
cth.next.ServeHTTP(rw, req)
return
}
}
if !util.GetPolicyChecker().ContentTrustEnabled(img.ProjectName) {
cth.next.ServeHTTP(rw, req)

View File

@ -2,15 +2,20 @@ package regtoken
import (
"context"
"github.com/docker/distribution/registry/auth"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/core/middlewares/util"
pkg_token "github.com/goharbor/harbor/src/pkg/token"
"github.com/goharbor/harbor/src/pkg/token/claim"
"github.com/goharbor/harbor/src/pkg/token/claims/registry"
"net/http"
"strings"
)
// regTokenHandler is responsible for decoding the registry token in the docker pull request header,
// as harbor adds customized claims action into registry auth token, the middlerware is for decode it and write it into
// request context, then for other middlerwares in chain to use it to bypass request validation.
type regTokenHandler struct {
next http.Handler
}
@ -24,6 +29,7 @@ func New(next http.Handler) http.Handler {
// ServeHTTP ...
func (r *regTokenHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
imgRaw := req.Context().Value(util.ImageInfoCtxKey)
if imgRaw == nil || !config.WithClair() {
r.next.ServeHTTP(rw, req)
@ -42,14 +48,30 @@ func (r *regTokenHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
rawToken := parts[1]
opt := pkg_token.DefaultTokenOptions()
rClaims := &claim.Registry{}
rtk, err := pkg_token.Parse(opt, rawToken, rClaims)
regTK, err := pkg_token.Parse(opt, rawToken, &registry.Claim{})
if err != nil {
log.Debug("failed to decode reg token: %v, the error is skipped and round the request to native registry.", err)
log.Debugf("failed to decode reg token: %v, the error is skipped and round the request to native registry.", err)
r.next.ServeHTTP(rw, req)
return
}
ctx := context.WithValue(req.Context(), util.PolicyCheckCtxKey, rtk.Claims.(*claim.Registry).PolicyCheck)
req = req.WithContext(ctx)
accessItems := []auth.Access{}
accessItems = append(accessItems, auth.Access{
Resource: auth.Resource{
Type: "repository",
Name: img.Repository,
},
Action: rbac.ActionScannerPull.String(),
})
accessSet := regTK.Claims.(*registry.Claim).GetAccessSet()
for _, access := range accessItems {
if accessSet.Contains(access) {
ctx := context.WithValue(req.Context(), util.ByPassPolicyCheckCtxKey, true)
req = req.WithContext(ctx)
r.next.ServeHTTP(rw, req)
return
}
}
r.next.ServeHTTP(rw, req)
}

View File

@ -49,8 +49,8 @@ type contextKey string
const (
// ImageInfoCtxKey the context key for image information
ImageInfoCtxKey = contextKey("ImageInfo")
// PolicyCheckCtxKey the context key for image information
PolicyCheckCtxKey = contextKey("PolicyCheck")
// ByPassPolicyCheckCtxKey the context key for robot account to bypass the pull policy check.
ByPassPolicyCheckCtxKey = contextKey("ByPassPolicyCheck")
// TokenUsername ...
// TODO: temp solution, remove after vmware/harbor#2242 is resolved.
TokenUsername = "harbor-core"

View File

@ -53,10 +53,12 @@ func (vh vulnerableHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request)
}
// Token bypass policy check
policyCheck := req.Context().Value(util.PolicyCheckCtxKey)
if policyCheck != nil && !policyCheck.(bool) {
vh.next.ServeHTTP(rw, req)
return
if bypassPC := req.Context().Value(util.ByPassPolicyCheckCtxKey); bypassPC != nil {
bypassPolicyCheck, ok := bypassPC.(bool)
if ok && bypassPolicyCheck {
vh.next.ServeHTTP(rw, req)
return
}
}
// Is vulnerable policy set?

View File

@ -42,12 +42,6 @@ func init() {
privateKey = config.TokenPrivateKeyPath()
}
// ClaimSet ...
type ClaimSet struct {
token.ClaimSet
PolicyCheck bool `json:"policy_check"`
}
// GetResourceActions ...
func GetResourceActions(scopes []string) []*token.ResourceActions {
log.Debugf("scopes: %+v", scopes)
@ -106,7 +100,7 @@ func filterAccess(access []*token.ResourceActions, ctx security.Context,
}
// MakeToken makes a valid jwt token based on parms.
func MakeToken(username, service string, access []*token.ResourceActions, policyCheck bool) (*models.Token, error) {
func MakeToken(username, service string, access []*token.ResourceActions) (*models.Token, error) {
pk, err := libtrust.LoadKeyFile(privateKey)
if err != nil {
return nil, err
@ -116,7 +110,7 @@ func MakeToken(username, service string, access []*token.ResourceActions, policy
return nil, err
}
tk, expiresIn, issuedAt, err := makeTokenCore(issuer, username, service, expiration, access, pk, policyCheck)
tk, expiresIn, issuedAt, err := makeTokenCore(issuer, username, service, expiration, access, pk)
if err != nil {
return nil, err
}
@ -139,12 +133,15 @@ func permToActions(p string) []string {
if strings.Contains(p, "R") {
res = append(res, "pull")
}
if strings.Contains(p, "S") {
res = append(res, "scannerpull")
}
return res
}
// make token core
func makeTokenCore(issuer, subject, audience string, expiration int,
access []*token.ResourceActions, signingKey libtrust.PrivateKey, policyCheck bool) (t *token.Token, expiresIn int, issuedAt *time.Time, err error) {
access []*token.ResourceActions, signingKey libtrust.PrivateKey) (t *token.Token, expiresIn int, issuedAt *time.Time, err error) {
joseHeader := &token.Header{
Type: "JWT",
@ -161,18 +158,15 @@ func makeTokenCore(issuer, subject, audience string, expiration int,
issuedAt = &now
expiresIn = expiration * 60
claimSet := &ClaimSet{
ClaimSet: token.ClaimSet{
Issuer: issuer,
Subject: subject,
Audience: audience,
Expiration: now.Add(time.Duration(expiration) * time.Minute).Unix(),
NotBefore: now.Unix(),
IssuedAt: now.Unix(),
JWTID: jwtID,
Access: access,
},
PolicyCheck: policyCheck,
claimSet := &token.ClaimSet{
Issuer: issuer,
Subject: subject,
Audience: audience,
Expiration: now.Add(time.Duration(expiration) * time.Minute).Unix(),
NotBefore: now.Unix(),
IssuedAt: now.Unix(),
JWTID: jwtID,
Access: access,
}
var joseHeaderBytes, claimSetBytes []byte

View File

@ -28,6 +28,7 @@ import (
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/core/filter"
"github.com/goharbor/harbor/src/core/promgr"
"net/http/httputil"
)
var creatorMap map[string]Creator
@ -154,7 +155,7 @@ type repositoryFilter struct {
func (rep repositoryFilter) filter(ctx security.Context, pm promgr.ProjectManager,
a *token.ResourceActions) error {
// clear action list to assign to new acess element after perm check.
// clear action list to assign to new access element after perm check.
img, err := rep.parser.parse(a.Name)
if err != nil {
return err
@ -180,6 +181,9 @@ func (rep repositoryFilter) filter(ctx security.Context, pm promgr.ProjectManage
} else if ctx.Can(rbac.ActionPull, resource) {
permission = "R"
}
if ctx.Can(rbac.ActionScannerPull, resource) {
permission = "S"
}
a.Actions = permToActions(permission)
return nil
@ -200,6 +204,7 @@ func (g generalCreator) Create(r *http.Request) (*models.Token, error) {
var err error
scopes := parseScopes(r.URL)
log.Debugf("scopes: %v", scopes)
httputil.DumpRequest(r, true)
ctx, err := filter.GetSecurityContext(r)
if err != nil {
@ -222,7 +227,7 @@ func (g generalCreator) Create(r *http.Request) (*models.Token, error) {
if err != nil {
return nil, err
}
return MakeToken(ctx.GetUsername(), g.service, access, ctx.PolicyCheck())
return MakeToken(ctx.GetUsername(), g.service, access)
}
func parseScopes(u *url.URL) []string {

View File

@ -119,8 +119,7 @@ func getPublicKey(crtPath string) (*rsa.PublicKey, error) {
type harborClaims struct {
jwt.StandardClaims
// Private claims
Access []*token.ResourceActions `json:"access"`
PolicyCheck bool `json:"policy_check"`
Access []*token.ResourceActions `json:"access"`
}
func TestMakeToken(t *testing.T) {
@ -134,7 +133,7 @@ func TestMakeToken(t *testing.T) {
}}
svc := "harbor-registry"
u := "tester"
tokenJSON, err := MakeToken(u, svc, ra, true)
tokenJSON, err := MakeToken(u, svc, ra)
if err != nil {
t.Errorf("Error while making token: %v", err)
}
@ -155,7 +154,6 @@ func TestMakeToken(t *testing.T) {
t.Errorf("Error while parsing the token: %v", err)
}
claims := tok.Claims.(*harborClaims)
assert.True(t, claims.PolicyCheck)
assert.Equal(t, *(claims.Access[0]), *(ra[0]), "Access mismatch")
assert.Equal(t, claims.Audience, svc, "Audience mismatch")
}
@ -244,9 +242,6 @@ func (f *fakeSecurityContext) IsSysAdmin() bool {
func (f *fakeSecurityContext) IsSolutionUser() bool {
return false
}
func (f *fakeSecurityContext) PolicyCheck() bool {
return false
}
func (f *fakeSecurityContext) Can(action rbac.Action, resource rbac.Resource) bool {
return false
}

View File

@ -4,12 +4,13 @@ import (
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/pkg/q"
"github.com/goharbor/harbor/src/pkg/robot/model"
"github.com/goharbor/harbor/src/pkg/token"
"github.com/goharbor/harbor/src/pkg/token/claim"
robot_claim "github.com/goharbor/harbor/src/pkg/token/claims/robot"
"github.com/pkg/errors"
"time"
)
@ -71,6 +72,12 @@ func (d *DefaultAPIController) CreateRobotAccount(robotReq *model.RobotCreate) (
ExpiresAt: expiresAt,
Visible: robotReq.Visible,
}
if robotReq.ByPassPolicyCheck {
robotReq.Access = append(robotReq.Access, &rbac.Policy{
Resource: rbac.NewProjectNamespace(robotReq.ProjectID).Resource(rbac.ResourceRepository),
Action: rbac.ActionScannerPull,
})
}
id, err := d.manager.CreateRobotAccount(robot)
if err != nil {
return nil, err
@ -79,11 +86,10 @@ func (d *DefaultAPIController) CreateRobotAccount(robotReq *model.RobotCreate) (
// generate the token, and return it with response data.
// token is not stored in the database.
opt := token.DefaultTokenOptions()
rClaims := &claim.Robot{
TokenID: id,
ProjectID: robotReq.ProjectID,
Access: robotReq.Access,
PolicyCheck: robotReq.PolicyCheck,
rClaims := &robot_claim.Claim{
TokenID: id,
ProjectID: robotReq.ProjectID,
Access: robotReq.Access,
StandardClaims: jwt.StandardClaims{
IssuedAt: time.Now().UTC().Unix(),
ExpiresAt: expiresAt,

View File

@ -45,13 +45,13 @@ type RobotQuery struct {
// RobotCreate ...
type RobotCreate struct {
Name string `json:"name"`
ProjectID int64 `json:"pid"`
Description string `json:"description"`
Disabled bool `json:"disabled"`
Visible bool `json:"-"`
PolicyCheck bool `json:"-"`
Access []*rbac.Policy `json:"access"`
Name string `json:"name"`
ProjectID int64 `json:"pid"`
Description string `json:"description"`
Disabled bool `json:"disabled"`
Visible bool `json:"-"`
ByPassPolicyCheck bool `json:"-"`
Access []*rbac.Policy `json:"access"`
}
// Pagination ...

View File

@ -1,22 +0,0 @@
package claim
import (
"github.com/dgrijalva/jwt-go"
"github.com/docker/distribution/registry/auth/token"
)
// Registry implements the interface of jwt.Claims
type Registry struct {
jwt.StandardClaims
PolicyCheck bool `json:"policy_check"`
Access []*token.ResourceActions `json:"access"`
}
// Valid valid the standard claims
func (rc *Registry) Valid() error {
stdErr := rc.StandardClaims.Valid()
if stdErr != nil {
return stdErr
}
return nil
}

View File

@ -0,0 +1,18 @@
package registry
import (
"github.com/docker/distribution/registry/auth"
)
// AccessSet ...
type AccessSet map[auth.Resource]actionSet
// Contains ...
func (s AccessSet) Contains(access auth.Access) bool {
actionSet, ok := s[access.Resource]
if ok {
return actionSet.contains(access.Action)
}
return false
}

View File

@ -0,0 +1,14 @@
package registry
// actionSet is a special type of stringSet.
type actionSet struct {
stringSet
}
func newActionSet(actions ...string) actionSet {
return actionSet{newStringSet(actions...)}
}
func (s actionSet) contains(action string) bool {
return s.stringSet.contains(action)
}

View File

@ -0,0 +1,42 @@
package registry
import (
"github.com/dgrijalva/jwt-go"
"github.com/docker/distribution/registry/auth"
"github.com/docker/distribution/registry/auth/token"
)
// Claim implements the interface of jwt.Claims
type Claim struct {
jwt.StandardClaims
Access []*token.ResourceActions `json:"access"`
}
// Valid valid the standard claims
func (rc *Claim) Valid() error {
stdErr := rc.StandardClaims.Valid()
if stdErr != nil {
return stdErr
}
return nil
}
// GetAccessSet ...
func (rc *Claim) GetAccessSet() AccessSet {
accessSet := make(AccessSet, len(rc.Access))
for _, resourceActions := range rc.Access {
resource := auth.Resource{
Type: resourceActions.Type,
Name: resourceActions.Name,
}
set, exists := accessSet[resource]
if !exists {
set = newActionSet()
accessSet[resource] = set
}
for _, action := range resourceActions.Actions {
set.add(action)
}
}
return accessSet
}

View File

@ -0,0 +1,21 @@
package registry
// StringSet is a useful type for looking up strings.
type stringSet map[string]struct{}
func newStringSet(keys ...string) stringSet {
ss := make(stringSet, len(keys))
ss.add(keys...)
return ss
}
func (ss stringSet) add(keys ...string) {
for _, key := range keys {
ss[key] = struct{}{}
}
}
func (ss stringSet) contains(key string) bool {
_, ok := ss[key]
return ok
}

View File

@ -1,4 +1,4 @@
package claim
package robot
import (
"errors"
@ -6,17 +6,16 @@ import (
"github.com/goharbor/harbor/src/common/rbac"
)
// Robot implements the interface of jwt.Claims
type Robot struct {
// Claim implements the interface of jwt.Claims
type Claim struct {
jwt.StandardClaims
TokenID int64 `json:"id"`
ProjectID int64 `json:"pid"`
PolicyCheck bool `json:"policy_check"`
Access []*rbac.Policy `json:"access"`
TokenID int64 `json:"id"`
ProjectID int64 `json:"pid"`
Access []*rbac.Policy `json:"access"`
}
// Valid valid the claims "tokenID, projectID and access".
func (rc Robot) Valid() error {
func (rc Claim) Valid() error {
if rc.TokenID < 0 {
return errors.New("Token id must an valid INT")
}

View File

@ -1,4 +1,4 @@
package claim
package robot
import (
"github.com/goharbor/harbor/src/common/rbac"
@ -15,7 +15,7 @@ func TestValid(t *testing.T) {
policies := []*rbac.Policy{}
policies = append(policies, rbacPolicy)
rClaims := &Robot{
rClaims := &Claim{
TokenID: 1,
ProjectID: 2,
Access: policies,
@ -32,7 +32,7 @@ func TestUnValidTokenID(t *testing.T) {
policies := []*rbac.Policy{}
policies = append(policies, rbacPolicy)
rClaims := &Robot{
rClaims := &Claim{
TokenID: -1,
ProjectID: 2,
Access: policies,
@ -49,7 +49,7 @@ func TestUnValidProjectID(t *testing.T) {
policies := []*rbac.Policy{}
policies = append(policies, rbacPolicy)
rClaims := &Robot{
rClaims := &Claim{
TokenID: 1,
ProjectID: -2,
Access: policies,
@ -59,7 +59,7 @@ func TestUnValidProjectID(t *testing.T) {
func TestUnValidPolicy(t *testing.T) {
rClaims := &Robot{
rClaims := &Claim{
TokenID: 1,
ProjectID: 2,
Access: nil,

View File

@ -11,7 +11,7 @@ func TestNewOptions(t *testing.T) {
defaultOpt := DefaultTokenOptions()
assert.NotNil(t, defaultOpt)
assert.Equal(t, defaultOpt.SignMethod, jwt.GetSigningMethod("RS256"))
assert.Equal(t, defaultOpt.Issuer, "harbor-token-issuer")
assert.Equal(t, defaultOpt.Issuer, "harbor-token-defaultIssuer")
assert.Equal(t, defaultOpt.TTL, 60*time.Minute)
}

View File

@ -8,7 +8,7 @@ import (
"github.com/dgrijalva/jwt-go"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/pkg/token/claim"
robot_claim "github.com/goharbor/harbor/src/pkg/token/claims/robot"
"github.com/stretchr/testify/assert"
)
@ -35,7 +35,7 @@ func TestNew(t *testing.T) {
projectID := int64(321)
tokenExpiration := time.Duration(10) * 24 * time.Hour
expiresAt := time.Now().UTC().Add(tokenExpiration).Unix()
robot := claim.Robot{
robot := robot_claim.Claim{
TokenID: tokenID,
ProjectID: projectID,
Access: policies,
@ -64,7 +64,7 @@ func TestRaw(t *testing.T) {
tokenExpiration := time.Duration(10) * 24 * time.Hour
expiresAt := time.Now().UTC().Add(tokenExpiration).Unix()
robot := claim.Robot{
robot := robot_claim.Claim{
TokenID: tokenID,
ProjectID: projectID,
Access: policies,
@ -82,7 +82,7 @@ func TestRaw(t *testing.T) {
func TestParseWithClaims(t *testing.T) {
rawTk := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MTIzLCJQcm9qZWN0SUQiOjAsIkFjY2VzcyI6W3siUmVzb3VyY2UiOiIvcHJvamVjdC9saWJyYXkvcmVwb3NpdG9yeSIsIkFjdGlvbiI6InB1bGwiLCJFZmZlY3QiOiIifV0sIlN0YW5kYXJkQ2xhaW1zIjp7ImV4cCI6MTU0ODE0MDIyOSwiaXNzIjoiaGFyYm9yLXRva2VuLWlzc3VlciJ9fQ.Jc3qSKN4SJVUzAvBvemVpRcSOZaHlu0Avqms04qzPm4ru9-r9IRIl3mnSkI6m9XkzLUeJ7Kiwyw63ghngnVKw_PupeclOGC6s3TK5Cfmo4h-lflecXjZWwyy-dtH_e7Us_ItS-R3nXDJtzSLEpsGHCcAj-1X2s93RB2qD8LNSylvYeDezVkTzqRzzfawPJheKKh9JTrz-3eUxCwQard9-xjlwvfUYULoHTn9npNAUq4-jqhipW4uE8HL-ym33AGF57la8U0RO11hmDM5K8-PiYknbqJ_oONeS3HBNym2pEFeGjtTv2co213wl4T5lemlg4SGolMBuJ03L7_beVZ0o-MKTkKDqDwJalb6_PM-7u3RbxC9IzJMiwZKIPnD3FvV10iPxUUQHaH8Jz5UZ2pFIhi_8BNnlBfT0JOPFVYATtLjHMczZelj2YvAeR1UHBzq3E0jPpjjwlqIFgaHCaN_KMwEvadTo_Fi2sEH4pNGP7M3yehU_72oLJQgF4paJarsmEoij6ZtPs6xekBz1fccVitq_8WNIz9aeCUdkUBRwI5QKw1RdW4ua-w74ld5MZStWJA8veyoLkEb_Q9eq2oAj5KWFjJbW5-ltiIfM8gxKflsrkWAidYGcEIYcuXr7UdqEKXxtPiWM0xb3B91ovYvO5402bn3f9-UGtlcestxNHA"
rClaims := &claim.Robot{}
rClaims := &robot_claim.Claim{}
_, _ = Parse(DefaultTokenOptions(), rawTk, rClaims)
assert.Equal(t, int64(123), rClaims.TokenID)
assert.Equal(t, int64(0), rClaims.ProjectID)