mirror of
https://github.com/goharbor/harbor
synced 2024-09-20 14:45:37 +00:00
Merge pull request #7004 from wy65701436/robot-expiration-time
add expiration of robot account
This commit is contained in:
commit
f69e4f9a4a
|
@ -4734,6 +4734,9 @@ definitions:
|
|||
description:
|
||||
type: string
|
||||
description: The description of robot account
|
||||
expiresat:
|
||||
type: integer
|
||||
description: The expiration of robot account (in seconds)
|
||||
project_id:
|
||||
type: integer
|
||||
description: The project id of robot account
|
||||
|
|
|
@ -3,6 +3,7 @@ CREATE TABLE robot (
|
|||
name varchar(255),
|
||||
description varchar(1024),
|
||||
project_id int,
|
||||
expiresat bigint,
|
||||
disabled boolean DEFAULT false NOT NULL,
|
||||
creation_time timestamp default CURRENT_TIMESTAMP,
|
||||
update_time timestamp default CURRENT_TIMESTAMP,
|
||||
|
|
|
@ -51,7 +51,6 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
|
||||
// ConfigList - All configure items used in harbor
|
||||
// Steps to onboard a new setting
|
||||
// 1. Add configure item in metadatalist.go
|
||||
|
@ -131,5 +130,7 @@ var (
|
|||
{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},
|
||||
// the unit of expiration is minute, 43200 minutes = 30 days
|
||||
{Name: "robot_token_duration", Scope: UserScope, Group: BasicGroup, EnvKey: "ROBOT_TOKEN_DURATION", DefaultValue: "43200", ItemType: &IntType{}, Editable: true},
|
||||
}
|
||||
)
|
||||
|
|
|
@ -117,6 +117,7 @@ const (
|
|||
DefaultRegistryCtlURL = "http://registryctl:8080"
|
||||
DefaultClairHealthCheckServerURL = "http://clair:6061"
|
||||
// Use this prefix to distinguish harbor user, the prefix contains a special character($), so it cannot be registered as a harbor user.
|
||||
RobotPrefix = "robot$"
|
||||
CoreConfigPath = "/api/internal/configurations"
|
||||
RobotPrefix = "robot$"
|
||||
CoreConfigPath = "/api/internal/configurations"
|
||||
RobotTokenDuration = "robot_token_duration"
|
||||
)
|
||||
|
|
|
@ -29,6 +29,7 @@ type Robot struct {
|
|||
Name string `orm:"column(name)" json:"name"`
|
||||
Description string `orm:"column(description)" json:"description"`
|
||||
ProjectID int64 `orm:"column(project_id)" json:"project_id"`
|
||||
ExpiresAt int64 `orm:"column(expiresat)" json:"expiresat"`
|
||||
Disabled bool `orm:"column(disabled)" json:"disabled"`
|
||||
CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"`
|
||||
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
|
||||
|
|
|
@ -25,5 +25,9 @@ func (rc RobotClaims) Valid() error {
|
|||
if rc.Access == nil {
|
||||
return errors.New("The access info cannot be nil")
|
||||
}
|
||||
stdErr := rc.StandardClaims.Valid()
|
||||
if stdErr != nil {
|
||||
return stdErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -19,14 +19,15 @@ type HToken struct {
|
|||
}
|
||||
|
||||
// New ...
|
||||
func New(tokenID, projectID int64, access []*rbac.Policy) (*HToken, error) {
|
||||
func New(tokenID, projectID, expiresAt int64, access []*rbac.Policy) (*HToken, error) {
|
||||
rClaims := &RobotClaims{
|
||||
TokenID: tokenID,
|
||||
ProjectID: projectID,
|
||||
Access: access,
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
ExpiresAt: time.Now().Add(DefaultOptions.TTL).Unix(),
|
||||
Issuer: DefaultOptions.Issuer,
|
||||
IssuedAt: time.Now().UTC().Unix(),
|
||||
ExpiresAt: expiresAt,
|
||||
Issuer: DefaultOptions().Issuer,
|
||||
},
|
||||
}
|
||||
err := rClaims.Valid()
|
||||
|
@ -34,13 +35,13 @@ func New(tokenID, projectID int64, access []*rbac.Policy) (*HToken, error) {
|
|||
return nil, err
|
||||
}
|
||||
return &HToken{
|
||||
Token: *jwt.NewWithClaims(DefaultOptions.SignMethod, rClaims),
|
||||
Token: *jwt.NewWithClaims(DefaultOptions().SignMethod, rClaims),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Raw get the Raw string of token
|
||||
func (htk *HToken) Raw() (string, error) {
|
||||
key, err := DefaultOptions.GetKey()
|
||||
key, err := DefaultOptions().GetKey()
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
@ -54,12 +55,12 @@ func (htk *HToken) Raw() (string, error) {
|
|||
|
||||
// ParseWithClaims ...
|
||||
func ParseWithClaims(rawToken string, claims jwt.Claims) (*HToken, error) {
|
||||
key, err := DefaultOptions.GetKey()
|
||||
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() {
|
||||
if token.Method.Alg() != DefaultOptions().SignMethod.Alg() {
|
||||
return nil, errors.New("invalid signing method")
|
||||
}
|
||||
switch k := key.(type) {
|
||||
|
@ -75,9 +76,10 @@ func ParseWithClaims(rawToken string, claims jwt.Claims) (*HToken, error) {
|
|||
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, err
|
||||
return nil, errors.New("invalid jwt token")
|
||||
}
|
||||
return &HToken{
|
||||
Token: *token,
|
||||
|
|
|
@ -3,6 +3,7 @@ package token
|
|||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/core/config"
|
||||
|
@ -30,7 +31,9 @@ func TestNew(t *testing.T) {
|
|||
|
||||
tokenID := int64(123)
|
||||
projectID := int64(321)
|
||||
token, err := New(tokenID, projectID, policies)
|
||||
tokenExpiration := time.Duration(10) * 24 * time.Hour
|
||||
expiresAt := time.Now().UTC().Add(tokenExpiration).Unix()
|
||||
token, err := New(tokenID, projectID, expiresAt, policies)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, token.Header["alg"], "RS256")
|
||||
|
@ -49,7 +52,9 @@ func TestRaw(t *testing.T) {
|
|||
tokenID := int64(123)
|
||||
projectID := int64(321)
|
||||
|
||||
token, err := New(tokenID, projectID, policies)
|
||||
tokenExpiration := time.Duration(10) * 24 * time.Hour
|
||||
expiresAt := time.Now().UTC().Add(tokenExpiration).Unix()
|
||||
token, err := New(tokenID, projectID, expiresAt, policies)
|
||||
assert.Nil(t, err)
|
||||
|
||||
rawTk, err := token.Raw()
|
||||
|
|
|
@ -16,12 +16,6 @@ const (
|
|||
signedMethod = "RS256"
|
||||
)
|
||||
|
||||
var (
|
||||
privateKey = config.TokenPrivateKeyPath()
|
||||
// DefaultOptions ...
|
||||
DefaultOptions = NewOptions()
|
||||
)
|
||||
|
||||
// Options ...
|
||||
type Options struct {
|
||||
SignMethod jwt.SigningMethod
|
||||
|
@ -31,9 +25,10 @@ type Options struct {
|
|||
Issuer string
|
||||
}
|
||||
|
||||
// NewOptions ...
|
||||
func NewOptions() *Options {
|
||||
privateKey, err := ioutil.ReadFile(privateKey)
|
||||
// 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
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
func TestNewOptions(t *testing.T) {
|
||||
defaultOpt := DefaultOptions
|
||||
defaultOpt := DefaultOptions()
|
||||
assert.NotNil(t, defaultOpt)
|
||||
assert.Equal(t, defaultOpt.SignMethod, jwt.GetSigningMethod("RS256"))
|
||||
assert.Equal(t, defaultOpt.Issuer, "harbor-token-issuer")
|
||||
|
@ -16,7 +16,7 @@ func TestNewOptions(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetKey(t *testing.T) {
|
||||
defaultOpt := DefaultOptions
|
||||
defaultOpt := DefaultOptions()
|
||||
key, err := defaultOpt.GetKey()
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, key)
|
||||
|
|
|
@ -16,14 +16,16 @@ package api
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/common/token"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/goharbor/harbor/src/core/config"
|
||||
"time"
|
||||
)
|
||||
|
||||
// RobotAPI ...
|
||||
|
@ -104,6 +106,9 @@ func (r *RobotAPI) Post() {
|
|||
}
|
||||
|
||||
var robotReq models.RobotReq
|
||||
// Token duration in minutes
|
||||
tokenDuration := time.Duration(config.RobotTokenDuration()) * time.Minute
|
||||
expiresAt := time.Now().UTC().Add(tokenDuration).Unix()
|
||||
r.DecodeJSONReq(&robotReq)
|
||||
createdName := common.RobotPrefix + robotReq.Name
|
||||
|
||||
|
@ -112,6 +117,7 @@ func (r *RobotAPI) Post() {
|
|||
Name: createdName,
|
||||
Description: robotReq.Description,
|
||||
ProjectID: r.project.ProjectID,
|
||||
ExpiresAt: expiresAt,
|
||||
}
|
||||
id, err := dao.AddRobot(&robot)
|
||||
if err != nil {
|
||||
|
@ -125,7 +131,7 @@ func (r *RobotAPI) Post() {
|
|||
|
||||
// generate the token, and return it with response data.
|
||||
// token is not stored in the database.
|
||||
jwtToken, err := token.New(id, r.project.ProjectID, robotReq.Access)
|
||||
jwtToken, err := token.New(id, r.project.ProjectID, expiresAt, robotReq.Access)
|
||||
if err != nil {
|
||||
r.HandleInternalServerError(fmt.Sprintf("failed to valid parameters to generate token for robot account, %v", err))
|
||||
err := dao.DeleteRobot(id)
|
||||
|
|
|
@ -225,6 +225,11 @@ func TokenExpiration() (int, error) {
|
|||
return cfgMgr.Get(common.TokenExpiration).GetInt(), nil
|
||||
}
|
||||
|
||||
// RobotTokenDuration returns the token expiration time of robot account (in minute)
|
||||
func RobotTokenDuration() int {
|
||||
return cfgMgr.Get(common.RobotTokenDuration).GetInt()
|
||||
}
|
||||
|
||||
// ExtEndpoint returns the external URL of Harbor: protocol://host:port
|
||||
func ExtEndpoint() (string, error) {
|
||||
return cfgMgr.Get(common.ExtEndpoint).GetString(), nil
|
||||
|
|
|
@ -97,6 +97,9 @@ func TestConfig(t *testing.T) {
|
|||
t.Fatalf("failed to get token expiration: %v", err)
|
||||
}
|
||||
|
||||
tkExp := RobotTokenDuration()
|
||||
assert.Equal(tkExp, 43200)
|
||||
|
||||
if _, err := ExtEndpoint(); err != nil {
|
||||
t.Fatalf("failed to get domain name: %v", err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user