1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-02 20:35:25 +02:00

feat(kubeconfig): Introduce the ability to change the expiry of a kubeconfig EE-1153 (#5421)

* feat(kubeconfig) EE-1153 Introduce the ability to change the expiry of a kubeconfig

* feat(kubeconfig) EE-1153 pr feedback update

* feat(kubeconfig) EE-1153 code cleanup

Co-authored-by: Simon Meng <simon.meng@portainer.io>
This commit is contained in:
cong meng 2021-09-01 09:23:21 +12:00 committed by GitHub
parent c597ae96e2
commit 35013e7b6a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 221 additions and 37 deletions

View file

@ -16,6 +16,7 @@ import (
type Service struct {
secret []byte
userSessionTimeout time.Duration
dataStore portainer.DataStore
}
type claims struct {
@ -31,7 +32,7 @@ var (
)
// NewService initializes a new service. It will generate a random key that will be used to sign JWT tokens.
func NewService(userSessionDuration string) (*Service, error) {
func NewService(userSessionDuration string, dataStore portainer.DataStore) (*Service, error) {
userSessionTimeout, err := time.ParseDuration(userSessionDuration)
if err != nil {
return nil, err
@ -45,19 +46,28 @@ func NewService(userSessionDuration string) (*Service, error) {
service := &Service{
secret,
userSessionTimeout,
dataStore,
}
return service, nil
}
func (service *Service) defaultExpireAt() (int64) {
return time.Now().Add(service.userSessionTimeout).Unix()
}
// GenerateToken generates a new JWT token.
func (service *Service) GenerateToken(data *portainer.TokenData) (string, error) {
return service.generateSignedToken(data, nil)
return service.generateSignedToken(data, service.defaultExpireAt())
}
// GenerateTokenForOAuth generates a new JWT for OAuth login
// token expiry time from the OAuth provider is considered
func (service *Service) GenerateTokenForOAuth(data *portainer.TokenData, expiryTime *time.Time) (string, error) {
return service.generateSignedToken(data, expiryTime)
expireAt := service.defaultExpireAt()
if expiryTime != nil && !expiryTime.IsZero() {
expireAt = expiryTime.Unix()
}
return service.generateSignedToken(data, expireAt)
}
// ParseAndVerifyToken parses a JWT token and verify its validity. It returns an error if token is invalid.
@ -88,17 +98,13 @@ func (service *Service) SetUserSessionDuration(userSessionDuration time.Duration
service.userSessionTimeout = userSessionDuration
}
func (service *Service) generateSignedToken(data *portainer.TokenData, expiryTime *time.Time) (string, error) {
expireToken := time.Now().Add(service.userSessionTimeout).Unix()
if expiryTime != nil && !expiryTime.IsZero() {
expireToken = expiryTime.Unix()
}
func (service *Service) generateSignedToken(data *portainer.TokenData, expiresAt int64) (string, error) {
cl := claims{
UserID: int(data.ID),
Username: data.Username,
Role: int(data.Role),
StandardClaims: jwt.StandardClaims{
ExpiresAt: expireToken,
ExpiresAt: expiresAt,
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, cl)

26
api/jwt/jwt_kubeconfig.go Normal file
View file

@ -0,0 +1,26 @@
package jwt
import (
portainer "github.com/portainer/portainer/api"
"time"
)
// GenerateTokenForKubeconfig generates a new JWT token for Kubeconfig
func (service *Service) GenerateTokenForKubeconfig(data *portainer.TokenData) (string, error) {
settings, err := service.dataStore.Settings().Settings()
if err != nil {
return "", err
}
expiryDuration, err := time.ParseDuration(settings.KubeconfigExpiry)
if err != nil {
return "", err
}
expiryAt := time.Now().Add(expiryDuration).Unix()
if expiryDuration == time.Duration(0) {
expiryAt = 0
}
return service.generateSignedToken(data, expiryAt)
}

View file

@ -0,0 +1,81 @@
package jwt
import (
"github.com/dgrijalva/jwt-go"
portainer "github.com/portainer/portainer/api"
i "github.com/portainer/portainer/api/internal/testhelpers"
"github.com/stretchr/testify/assert"
"testing"
)
func TestService_GenerateTokenForKubeconfig(t *testing.T) {
type fields struct {
userSessionTimeout string
dataStore portainer.DataStore
}
type args struct {
data *portainer.TokenData
}
mySettings := &portainer.Settings{
KubeconfigExpiry: "0",
}
myFields := fields{
userSessionTimeout: "24h",
dataStore: i.NewDatastore(i.WithSettings(mySettings)),
}
myTokenData := &portainer.TokenData{
Username: "Joe",
ID: 1,
Role: 1,
}
myArgs := args{
data: myTokenData,
}
tests := []struct {
name string
fields fields
args args
wantExpiresAt int64
wantErr bool
}{
{
name: "kubeconfig no expiry",
fields: myFields,
args: myArgs,
wantExpiresAt: 0,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
service, err := NewService(tt.fields.userSessionTimeout, tt.fields.dataStore)
assert.NoError(t, err, "failed to create a copy of service")
got, err := service.GenerateTokenForKubeconfig(tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("GenerateTokenForKubeconfig() error = %v, wantErr %v", err, tt.wantErr)
return
}
parsedToken, err := jwt.ParseWithClaims(got, &claims{}, func(token *jwt.Token) (interface{}, error) {
return service.secret, nil
})
assert.NoError(t, err, "failed to parse generated token")
tokenClaims, ok := parsedToken.Claims.(*claims)
assert.Equal(t, true, ok, "failed to claims out of generated ticket")
assert.Equal(t, myTokenData.Username, tokenClaims.Username)
assert.Equal(t, int(myTokenData.ID), tokenClaims.UserID)
assert.Equal(t, int(myTokenData.Role), tokenClaims.Role)
assert.Equal(t, tt.wantExpiresAt, tokenClaims.ExpiresAt)
})
}
}

View file

@ -10,7 +10,7 @@ import (
)
func TestGenerateSignedToken(t *testing.T) {
svc, err := NewService("24h")
svc, err := NewService("24h", nil)
assert.NoError(t, err, "failed to create a copy of service")
token := &portainer.TokenData{
@ -18,9 +18,9 @@ func TestGenerateSignedToken(t *testing.T) {
ID: 1,
Role: 1,
}
expirtationTime := time.Now().Add(1 * time.Hour)
expiresAt := time.Now().Add(1 * time.Hour).Unix()
generatedToken, err := svc.generateSignedToken(token, &expirtationTime)
generatedToken, err := svc.generateSignedToken(token, expiresAt)
assert.NoError(t, err, "failed to generate a signed token")
parsedToken, err := jwt.ParseWithClaims(generatedToken, &claims{}, func(token *jwt.Token) (interface{}, error) {
@ -34,5 +34,5 @@ func TestGenerateSignedToken(t *testing.T) {
assert.Equal(t, token.Username, tokenClaims.Username)
assert.Equal(t, int(token.ID), tokenClaims.UserID)
assert.Equal(t, int(token.Role), tokenClaims.Role)
assert.Equal(t, expirtationTime.Unix(), tokenClaims.ExpiresAt)
assert.Equal(t, expiresAt, tokenClaims.ExpiresAt)
}