Skip to content

Commit

Permalink
Revert "Enable robot account to support scan pull case"
Browse files Browse the repository at this point in the history
  • Loading branch information
wy65701436 committed Oct 24, 2019
1 parent d18678a commit 4a3dc9a
Show file tree
Hide file tree
Showing 25 changed files with 146 additions and 473 deletions.
3 changes: 1 addition & 2 deletions src/common/rbac/const.go
Expand Up @@ -28,8 +28,7 @@ const (
ActionDelete = Action("delete")
ActionList = Action("list")

ActionOperate = Action("operate")
ActionScannerPull = Action("scanner-pull") // for robot account created by scanner to pull image, bypass the policy check
ActionOperate = Action("operate")
)

// const resource variables
Expand Down
18 changes: 6 additions & 12 deletions src/common/security/robot/robot.go
Expand Up @@ -47,22 +47,16 @@ func filterPolicies(namespace rbac.Namespace, policies []*rbac.Policy) []*rbac.P
return results
}

mp := getAllPolicies(namespace)
mp := map[string]bool{}
for _, policy := range project.GetAllPolicies(namespace) {
mp[policy.String()] = true
}

for _, policy := range policies {
if mp[policy.String()] {
results = append(results, policy)
}
}
return results
}

// getAllPolicies gets all of supported policies supported in project and external policies supported for robot account
func getAllPolicies(namespace rbac.Namespace) map[string]bool {
mp := map[string]bool{}
for _, policy := range project.GetAllPolicies(namespace) {
mp[policy.String()] = true
}
scannerPull := &rbac.Policy{Resource: namespace.Resource(rbac.ResourceRepository), Action: rbac.ActionScannerPull}
mp[scannerPull.String()] = true
return mp
return results
}
3 changes: 1 addition & 2 deletions src/common/security/robot/robot_test.go
Expand Up @@ -44,11 +44,10 @@ func TestGetPolicies(t *testing.T) {
func TestNewRobot(t *testing.T) {
policies := []*rbac.Policy{
{Resource: "/project/1/repository", Action: "pull"},
{Resource: "/project/1/repository", Action: "scanner-pull"},
{Resource: "/project/library/repository", Action: "pull"},
{Resource: "/project/library/repository", Action: "push"},
}

robot := NewRobot("test", rbac.NewProjectNamespace(1, false), policies)
assert.Len(t, robot.GetPolicies(), 2)
assert.Len(t, robot.GetPolicies(), 1)
}
@@ -1,21 +1,21 @@
package robot
package token

import (
"errors"
"github.com/dgrijalva/jwt-go"
"github.com/goharbor/harbor/src/common/rbac"
)

// Claim implements the interface of jwt.Claims
type Claim struct {
// RobotClaims implements the interface of jwt.Claims
type RobotClaims struct {
jwt.StandardClaims
TokenID int64 `json:"id"`
ProjectID int64 `json:"pid"`
Access []*rbac.Policy `json:"access"`
}

// Valid valid the claims "tokenID, projectID and access".
func (rc Claim) Valid() error {
func (rc RobotClaims) Valid() error {
if rc.TokenID < 0 {
return errors.New("Token id must an valid INT")
}
Expand Down
@@ -1,4 +1,4 @@
package robot
package token

import (
"github.com/goharbor/harbor/src/common/rbac"
Expand All @@ -15,7 +15,7 @@ func TestValid(t *testing.T) {
policies := []*rbac.Policy{}
policies = append(policies, rbacPolicy)

rClaims := &Claim{
rClaims := &RobotClaims{
TokenID: 1,
ProjectID: 2,
Access: policies,
Expand All @@ -32,7 +32,7 @@ func TestUnValidTokenID(t *testing.T) {
policies := []*rbac.Policy{}
policies = append(policies, rbacPolicy)

rClaims := &Claim{
rClaims := &RobotClaims{
TokenID: -1,
ProjectID: 2,
Access: policies,
Expand All @@ -49,7 +49,7 @@ func TestUnValidProjectID(t *testing.T) {
policies := []*rbac.Policy{}
policies = append(policies, rbacPolicy)

rClaims := &Claim{
rClaims := &RobotClaims{
TokenID: 1,
ProjectID: -2,
Access: policies,
Expand All @@ -59,7 +59,7 @@ func TestUnValidProjectID(t *testing.T) {

func TestUnValidPolicy(t *testing.T) {

rClaims := &Claim{
rClaims := &RobotClaims{
TokenID: 1,
ProjectID: 2,
Access: nil,
Expand Down
87 changes: 87 additions & 0 deletions src/common/token/htoken.go
@@ -0,0 +1,87 @@
package token

import (
"crypto/ecdsa"
"crypto/rsa"
"errors"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/utils/log"
"time"
)

// HToken htoken is a jwt token for harbor robot account,
// which contains the robot ID, project ID and the access permission for the project.
// It used for authn/authz for robot account in Harbor.
type HToken struct {
jwt.Token
}

// New ...
func New(tokenID, projectID, expiresAt int64, access []*rbac.Policy) (*HToken, error) {
rClaims := &RobotClaims{
TokenID: tokenID,
ProjectID: projectID,
Access: access,
StandardClaims: jwt.StandardClaims{
IssuedAt: time.Now().UTC().Unix(),
ExpiresAt: expiresAt,
Issuer: DefaultOptions().Issuer,
},
}
err := rClaims.Valid()
if err != nil {
return nil, err
}
return &HToken{
Token: *jwt.NewWithClaims(DefaultOptions().SignMethod, rClaims),
}, nil
}

// Raw get the Raw string of token
func (htk *HToken) Raw() (string, error) {
key, err := DefaultOptions().GetKey()
if err != nil {
return "", nil
}
raw, err := htk.Token.SignedString(key)
if err != nil {
log.Debugf(fmt.Sprintf("failed to issue token %v", err))
return "", err
}
return raw, err
}

// ParseWithClaims ...
func ParseWithClaims(rawToken string, claims jwt.Claims) (*HToken, error) {
key, err := DefaultOptions().GetKey()
if err != nil {
return nil, err
}
token, err := jwt.ParseWithClaims(rawToken, claims, func(token *jwt.Token) (interface{}, error) {
if token.Method.Alg() != DefaultOptions().SignMethod.Alg() {
return nil, errors.New("invalid signing method")
}
switch k := key.(type) {
case *rsa.PrivateKey:
return &k.PublicKey, nil
case *ecdsa.PrivateKey:
return &k.PublicKey, nil
default:
return key, nil
}
})
if err != nil {
log.Errorf(fmt.Sprintf("parse token error, %v", err))
return nil, err
}

if !token.Valid {
log.Errorf(fmt.Sprintf("invalid jwt token, %v", token))
return nil, errors.New("invalid jwt token")
}
return &HToken{
Token: *token,
}, nil
}
26 changes: 4 additions & 22 deletions src/pkg/token/token_test.go → src/common/token/htoken_test.go
Expand Up @@ -5,10 +5,8 @@ import (
"testing"
"time"

"github.com/dgrijalva/jwt-go"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/core/config"
robot_claim "github.com/goharbor/harbor/src/pkg/token/claims/robot"
"github.com/stretchr/testify/assert"
)

Expand All @@ -35,15 +33,7 @@ func TestNew(t *testing.T) {
projectID := int64(321)
tokenExpiration := time.Duration(10) * 24 * time.Hour
expiresAt := time.Now().UTC().Add(tokenExpiration).Unix()
robot := robot_claim.Claim{
TokenID: tokenID,
ProjectID: projectID,
Access: policies,
StandardClaims: jwt.StandardClaims{
ExpiresAt: expiresAt,
},
}
token, err := New(DefaultTokenOptions(), robot)
token, err := New(tokenID, projectID, expiresAt, policies)

assert.Nil(t, err)
assert.Equal(t, token.Header["alg"], "RS256")
Expand All @@ -64,15 +54,7 @@ func TestRaw(t *testing.T) {

tokenExpiration := time.Duration(10) * 24 * time.Hour
expiresAt := time.Now().UTC().Add(tokenExpiration).Unix()
robot := robot_claim.Claim{
TokenID: tokenID,
ProjectID: projectID,
Access: policies,
StandardClaims: jwt.StandardClaims{
ExpiresAt: expiresAt,
},
}
token, err := New(DefaultTokenOptions(), robot)
token, err := New(tokenID, projectID, expiresAt, policies)
assert.Nil(t, err)

rawTk, err := token.Raw()
Expand All @@ -82,8 +64,8 @@ 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 := &robot_claim.Claim{}
_, _ = Parse(DefaultTokenOptions(), rawTk, rClaims)
rClaims := &RobotClaims{}
_, _ = ParseWithClaims(rawTk, rClaims)
assert.Equal(t, int64(123), rClaims.TokenID)
assert.Equal(t, int64(0), rClaims.ProjectID)
assert.Equal(t, "/project/libray/repository", rClaims.Access[0].Resource.String())
Expand Down
40 changes: 20 additions & 20 deletions src/pkg/token/options.go → src/common/token/options.go
Expand Up @@ -11,9 +11,9 @@ import (
)

const (
defaultTTL = 60 * time.Minute
defaultIssuer = "harbor-token-defaultIssuer"
defaultSignedMethod = "RS256"
ttl = 60 * time.Minute
issuer = "harbor-token-issuer"
signedMethod = "RS256"
)

// Options ...
Expand All @@ -25,6 +25,23 @@ type Options struct {
Issuer string
}

// DefaultOptions ...
func DefaultOptions() *Options {
privateKeyFile := config.TokenPrivateKeyPath()
privateKey, err := ioutil.ReadFile(privateKeyFile)
if err != nil {
log.Errorf(fmt.Sprintf("failed to read private key %v", err))
return nil
}
opt := &Options{
SignMethod: jwt.GetSigningMethod(signedMethod),
PrivateKey: privateKey,
Issuer: issuer,
TTL: ttl,
}
return opt
}

// GetKey ...
func (o *Options) GetKey() (interface{}, error) {
var err error
Expand Down Expand Up @@ -59,20 +76,3 @@ func (o *Options) GetKey() (interface{}, error) {
return nil, fmt.Errorf(fmt.Sprintf("unsupported sign method, %s", o.SignMethod))
}
}

// DefaultTokenOptions ...
func DefaultTokenOptions() *Options {
privateKeyFile := config.TokenPrivateKeyPath()
privateKey, err := ioutil.ReadFile(privateKeyFile)
if err != nil {
log.Errorf(fmt.Sprintf("failed to read private key %v", err))
return nil
}
opt := &Options{
SignMethod: jwt.GetSigningMethod(defaultSignedMethod),
PrivateKey: privateKey,
Issuer: defaultIssuer,
TTL: defaultTTL,
}
return opt
}
Expand Up @@ -8,15 +8,15 @@ import (
)

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

func TestGetKey(t *testing.T) {
defaultOpt := DefaultTokenOptions()
defaultOpt := DefaultOptions()
key, err := defaultOpt.GetKey()
assert.Nil(t, err)
assert.NotNil(t, key)
Expand Down
6 changes: 3 additions & 3 deletions src/common/utils/oidc/helper.go
Expand Up @@ -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 claims of the group list is set in OIDCSetting.
// GroupsFromToken returns the list of group name in the token, the claim 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 {
Expand All @@ -217,7 +217,7 @@ func GroupsFromToken(token *gooidc.IDToken) []string {
}
setting := provider.setting.Load().(models.OIDCSetting)
if len(setting.GroupsClaim) == 0 {
log.Warning("Group claims is not set in OIDC setting returning empty group list.")
log.Warning("Group claim is not set in OIDC setting returning empty group list.")
return []string{}
}
var c map[string]interface{}
Expand All @@ -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 claims key: %s", claimMap, k)
log.Warningf("Unable to get groups from claims, claims: %+v, groups claim key: %s", claimMap, k)
return res
}
for _, e := range g {
Expand Down

0 comments on commit 4a3dc9a

Please sign in to comment.