mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-07 11:10:16 +01:00
deprecate version 1 robot account (#15296)
1, deprecate support for version 1 robot support, the robotv1 cannot be used anymore. 2, reserve the /project/{id_or_name}/robots api. After the PR, user cannot use the robotv1 to login, and do any interaction with Harbor, but still can view & delete them with UI or API. Signed-off-by: Wang Yan <wangyan@vmware.com>
This commit is contained in:
parent
3e502ec9a4
commit
f7a4401dcb
@ -15,60 +15,76 @@
|
|||||||
package security
|
package security
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
|
||||||
"github.com/goharbor/harbor/src/common/security"
|
"github.com/goharbor/harbor/src/common/security"
|
||||||
robotCtx "github.com/goharbor/harbor/src/common/security/robot"
|
robotCtx "github.com/goharbor/harbor/src/common/security/robot"
|
||||||
ctl_robot "github.com/goharbor/harbor/src/controller/robot"
|
"github.com/goharbor/harbor/src/common/utils"
|
||||||
|
robot_ctl "github.com/goharbor/harbor/src/controller/robot"
|
||||||
|
"github.com/goharbor/harbor/src/lib/config"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
pkg_token "github.com/goharbor/harbor/src/pkg/token"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
robot_claim "github.com/goharbor/harbor/src/pkg/token/claims/robot"
|
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/goharbor/harbor/src/pkg/robot/model"
|
||||||
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type robot struct{}
|
type robot struct{}
|
||||||
|
|
||||||
func (r *robot) Generate(req *http.Request) security.Context {
|
func (r *robot) Generate(req *http.Request) security.Context {
|
||||||
log := log.G(req.Context())
|
log := log.G(req.Context())
|
||||||
robotName, robotTk, ok := req.BasicAuth()
|
name, secret, ok := req.BasicAuth()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(robotName, common.RobotPrefix) {
|
if !strings.HasPrefix(name, config.RobotPrefix(req.Context())) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
rClaims := &robot_claim.Claim{}
|
// The robot name can be used as the unique identifier to locate robot as it contains the project name.
|
||||||
defaultOpt := pkg_token.DefaultTokenOptions()
|
robots, err := robot_ctl.Ctl.List(req.Context(), q.New(q.KeyWords{
|
||||||
if defaultOpt == nil {
|
"name": strings.TrimPrefix(name, config.RobotPrefix(req.Context())),
|
||||||
log.Error("failed to get default token options")
|
}), &robot_ctl.Option{
|
||||||
return nil
|
WithPermission: true,
|
||||||
}
|
})
|
||||||
rtk, err := pkg_token.Parse(defaultOpt, robotTk, rClaims)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("failed to decrypt robot token of v1 robot: %s, as: %v", robotName, err)
|
log.Errorf("failed to list robots: %v", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Do authn for robot account, as Harbor only stores the token ID, just validate the ID and disable.
|
if len(robots) == 0 {
|
||||||
ctr := ctl_robot.Ctl
|
|
||||||
robot, err := ctr.Get(req.Context(), rtk.Claims.(*robot_claim.Claim).TokenID, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed to get robot %s: %v", robotName, err)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if robot == nil {
|
|
||||||
log.Error("the token provided doesn't exist.")
|
robot := robots[0]
|
||||||
return nil
|
if utils.Encrypt(secret, robot.Salt, utils.SHA256) != robot.Secret {
|
||||||
}
|
log.Errorf("failed to authenticate robot account: %s", name)
|
||||||
if robotName != robot.Name {
|
|
||||||
log.Errorf("failed to authenticate : %v", robotName)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if robot.Disabled {
|
if robot.Disabled {
|
||||||
log.Errorf("the robot account %s is disabled", robot.Name)
|
log.Errorf("failed to authenticate disabled robot account: %s", name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
log.Debugf("a robot security context generated for request %s %s", req.Method, req.URL.Path)
|
now := time.Now().Unix()
|
||||||
|
if robot.ExpiresAt != -1 && robot.ExpiresAt <= now {
|
||||||
return robotCtx.NewSecurityContext(&robot.Robot, false, rtk.Claims.(*robot_claim.Claim).Access)
|
log.Errorf("the robot account is expired: %s", name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var accesses []*types.Policy
|
||||||
|
for _, p := range robot.Permissions {
|
||||||
|
for _, a := range p.Access {
|
||||||
|
accesses = append(accesses, &types.Policy{
|
||||||
|
Action: a.Action,
|
||||||
|
Effect: a.Effect,
|
||||||
|
Resource: types.Resource(fmt.Sprintf("%s/%s", p.Scope, a.Resource)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modelRobot := &model.Robot{
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
log.Infof("a robot security context generated for request %s %s", req.Method, req.URL.Path)
|
||||||
|
return robotCtx.NewSecurityContext(modelRobot, robot.Level == robot_ctl.LEVELSYSTEM, accesses)
|
||||||
}
|
}
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
package security
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/goharbor/harbor/src/common/security"
|
|
||||||
robotCtx "github.com/goharbor/harbor/src/common/security/robot"
|
|
||||||
"github.com/goharbor/harbor/src/common/utils"
|
|
||||||
robot_ctl "github.com/goharbor/harbor/src/controller/robot"
|
|
||||||
"github.com/goharbor/harbor/src/lib/config"
|
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/pkg/robot/model"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type robot2 struct{}
|
|
||||||
|
|
||||||
func (r *robot2) Generate(req *http.Request) security.Context {
|
|
||||||
log := log.G(req.Context())
|
|
||||||
name, secret, ok := req.BasicAuth()
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(name, config.RobotPrefix(req.Context())) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// The robot name can be used as the unique identifier to locate robot as it contains the project name.
|
|
||||||
robots, err := robot_ctl.Ctl.List(req.Context(), q.New(q.KeyWords{
|
|
||||||
"name": strings.TrimPrefix(name, config.RobotPrefix(req.Context())),
|
|
||||||
}), &robot_ctl.Option{
|
|
||||||
WithPermission: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed to list robots: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if len(robots) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
robot := robots[0]
|
|
||||||
if utils.Encrypt(secret, robot.Salt, utils.SHA256) != robot.Secret {
|
|
||||||
log.Errorf("failed to authenticate robot account: %s", name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if robot.Disabled {
|
|
||||||
log.Errorf("failed to authenticate disabled robot account: %s", name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
now := time.Now().Unix()
|
|
||||||
if robot.ExpiresAt != -1 && robot.ExpiresAt <= now {
|
|
||||||
log.Errorf("the robot account is expired: %s", name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var accesses []*types.Policy
|
|
||||||
for _, p := range robot.Permissions {
|
|
||||||
for _, a := range p.Access {
|
|
||||||
accesses = append(accesses, &types.Policy{
|
|
||||||
Action: a.Action,
|
|
||||||
Effect: a.Effect,
|
|
||||||
Resource: types.Resource(fmt.Sprintf("%s/%s", p.Scope, a.Resource)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
modelRobot := &model.Robot{
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
log.Infof("a robot2 security context generated for request %s %s", req.Method, req.URL.Path)
|
|
||||||
return robotCtx.NewSecurityContext(modelRobot, robot.Level == robot_ctl.LEVELSYSTEM, accesses)
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
package security
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
|
||||||
"github.com/goharbor/harbor/src/lib/config"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRobot2(t *testing.T) {
|
|
||||||
conf := map[string]interface{}{
|
|
||||||
common.RobotNamePrefix: "robot@",
|
|
||||||
}
|
|
||||||
config.InitWithSettings(conf)
|
|
||||||
|
|
||||||
robot := &robot2{}
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1/api/projects/", nil)
|
|
||||||
require.Nil(t, err)
|
|
||||||
req.SetBasicAuth("robot@est1", "Harbor12345")
|
|
||||||
ctx := robot.Generate(req)
|
|
||||||
assert.Nil(t, ctx)
|
|
||||||
}
|
|
@ -15,6 +15,8 @@
|
|||||||
package security
|
package security
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/goharbor/harbor/src/common"
|
||||||
|
"github.com/goharbor/harbor/src/lib/config"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -22,10 +24,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestRobot(t *testing.T) {
|
func TestRobot(t *testing.T) {
|
||||||
|
conf := map[string]interface{}{
|
||||||
|
common.RobotNamePrefix: "robot@",
|
||||||
|
}
|
||||||
|
config.InitWithSettings(conf)
|
||||||
|
|
||||||
robot := &robot{}
|
robot := &robot{}
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1/api/projects/", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1/api/projects/", nil)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
req.SetBasicAuth("robot$test1", "Harbor12345")
|
req.SetBasicAuth("robot@est1", "Harbor12345")
|
||||||
ctx := robot.Generate(req)
|
ctx := robot.Generate(req)
|
||||||
assert.Nil(t, ctx)
|
assert.Nil(t, ctx)
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,6 @@ var (
|
|||||||
&idToken{},
|
&idToken{},
|
||||||
&authProxy{},
|
&authProxy{},
|
||||||
&robot{},
|
&robot{},
|
||||||
&robot2{},
|
|
||||||
&basicAuth{},
|
&basicAuth{},
|
||||||
&session{},
|
&session{},
|
||||||
&proxyCacheSecret{},
|
&proxyCacheSecret{},
|
||||||
|
Loading…
Reference in New Issue
Block a user