Refactor authenticator
Signed-off-by: hongming <hongming@kubesphere.io>
This commit is contained in:
339
pkg/apiserver/authentication/token/issuer_test.go
Normal file
339
pkg/apiserver/authentication/token/issuer_test.go
Normal file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
|
||||
Copyright 2021 The KubeSphere Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package token
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/form3tech-oss/jwt-go"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
|
||||
)
|
||||
|
||||
const privateKeyData = `
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEoQIBAAKCAQEAnDK2bNmX+tBWY/JHll1T1LF3/6RTbJ2qUsvwZVuVP/XbmWeY
|
||||
vDZyTR+YL6JaqRC/NibphgCV0p6cKZNuoGCEHpS0Ix9ZZwkA8BhwrFwAU0O1Qmrv
|
||||
v7It3p0Lc9WKN7PBWDQnUIdeSWnAeSbmWETP8Y2e+vG/iusLojPJenEaiOiwzU8p
|
||||
3CGSh7IPBXF3aUeB3dgJuaiumDuzlp0Oe/xKvWo0faB2hFXi36KaLMpugNcbejKl
|
||||
R6w3jH5wJjto5XqTEpW4a77K4rt7CFXVGfcbLo+n/5j3oC0lw4KOy7OX0Qf1jY+x
|
||||
sa1Q+3UoDC02sQRf77uj3eITol8Spoo7wfJqmwIDAQABAoIBAGEArYIT7+p3j+8p
|
||||
+4NKGlGwlRFR/+0oTSp2NKj9o0bBbMtsJtJcDcgPoveSIDN2jwkWSVhK7MCMd/bp
|
||||
9H3s8p/7QZO+WEtAsDBrPS4NRLZxChRhTNsD0LC7Xu1k5B2LqLsaSIAeUVPONRYI
|
||||
Lm0K7wjYJq85iva+2c610p4Tt6LlxuOu41Zw7RAaW8nBoMdQzi19X+hUloogVo7S
|
||||
hid8gm2KUPY6xF+RpHGQ5OUND0d+2wBkHxbYNRIfYrxCKt8+dLykLzAmm+ScCfyG
|
||||
jKcNoRwW5s/3ttR7r7hn3whttydkper5YvxM3+EvL83H7JL11KHcHy/yPYv+2IxQ
|
||||
psvEtIECgYEAykCm/w58pdifLuWG1mwHCkdQ6wCd9gAHIDfaqbFhTcdwGGYXb1xQ
|
||||
3CHjkkX6rpB3rJReCxlGq01OemVNlpIGLhdnK87aX5pRVn2fHGaMoC2V5RWv3pyE
|
||||
3gJ41h9FtPX2juKFG9PNiR7FrtKPzQczfh2L1OMpLOXfPgxvo/fXBQsCgYEAxbTz
|
||||
mibb4F/TBVXMuSL7Pk9hBPlFgFIEUKbiqt+sKQCqSZPGjV5giDfQDGsJ5EqOkRg0
|
||||
qlCrKk+DW+d+Pmc4yo58cd2xPnQETholV19+dM3AGiy4BMjeUnJD+Dme7M/fhrlW
|
||||
IK/1ZErKSZ3nN20qeneIFltm6+4pgQ1HB9KwirECgYAy65wf0xHm32cUc41DJueO
|
||||
2u2wfPNIIDGrFuTinFoXLwM14V49F0z0X0Pga+X1VUIMHT6gJLj6H/iGMEMciZ8s
|
||||
s4+yI94u+7dGw1Hv4JG/Mjru9krVDSsWiiDKKA1wxgxRZQ6GNwkkYK78mN7Di/CW
|
||||
6/Fso9SWDTnrcU4aRifIiQKBgQCQ+kJwVfKCtIIPtX0sfeRzKs5gUVKP6JTVd6tb
|
||||
1i1u29gDoGPHIt/yw8rCcHOOfsXQzElCY2lA25HeAQFoTVUt5BKJhSIGRBksFKwx
|
||||
SAt5J6+pAgXnLE0rdDM3gTlzOnQVXS81RRLTeqygEzSMRncR2zll+5ybgcfZpJzj
|
||||
tbJT4QJ/Y02wfkm1dL/BFg520/otVeuC+Bt+YyWMVs867xLLzFci7tj6ZzlzMorQ
|
||||
PsSsOHhPx0g+Wl8K2+Edg3FQRZ1m0rQFAZn66jd96u85aA9NH/bw3A3VYUdVJyHh
|
||||
4ZgZLx9JMCkmRfa7Dp2mzoqGUC1cjNvm722baeMqXpHSXDP2Jg==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`
|
||||
|
||||
func TestNewIssuer(t *testing.T) {
|
||||
signKeyData := base64.StdEncoding.EncodeToString([]byte(privateKeyData))
|
||||
options := &authentication.Options{
|
||||
MaximumClockSkew: 10 * time.Second,
|
||||
JwtSecret: "test-secret",
|
||||
OAuthOptions: &oauth.Options{
|
||||
Issuer: "kubesphere",
|
||||
SignKeyData: signKeyData,
|
||||
},
|
||||
}
|
||||
got, err := NewIssuer(options)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
signKey, keyID, err := loadSignKey(options)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
want := &issuer{
|
||||
name: options.OAuthOptions.Issuer,
|
||||
secret: []byte(options.JwtSecret),
|
||||
maximumClockSkew: options.MaximumClockSkew,
|
||||
signKey: &jose.JSONWebKey{
|
||||
Key: signKey,
|
||||
KeyID: keyID,
|
||||
Algorithm: jwt.SigningMethodRS256.Alg(),
|
||||
Use: "sig",
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("NewIssuer() got = %v, want %v", got, want)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewIssuerGenerateSignKey(t *testing.T) {
|
||||
options := &authentication.Options{
|
||||
MaximumClockSkew: 10 * time.Second,
|
||||
JwtSecret: "test-secret",
|
||||
OAuthOptions: &oauth.Options{
|
||||
Issuer: "kubesphere",
|
||||
},
|
||||
}
|
||||
|
||||
got, err := NewIssuer(options)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
iss := got.(*issuer)
|
||||
assert.NotNil(t, iss.signKey)
|
||||
assert.NotNil(t, iss.signKey.Key)
|
||||
assert.NotNil(t, iss.signKey.KeyID)
|
||||
}
|
||||
|
||||
func Test_issuer_IssueTo(t *testing.T) {
|
||||
type fields struct {
|
||||
name string
|
||||
secret []byte
|
||||
maximumClockSkew time.Duration
|
||||
}
|
||||
type args struct {
|
||||
request *IssueRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *VerifiedResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "token is successfully issued",
|
||||
fields: fields{
|
||||
name: "kubesphere",
|
||||
secret: []byte("kubesphere"),
|
||||
maximumClockSkew: 0,
|
||||
},
|
||||
args: args{request: &IssueRequest{
|
||||
User: &user.DefaultInfo{
|
||||
Name: "user1",
|
||||
},
|
||||
Claims: Claims{
|
||||
TokenType: AccessToken,
|
||||
},
|
||||
ExpiresIn: 2 * time.Hour},
|
||||
},
|
||||
want: &VerifiedResponse{
|
||||
User: &user.DefaultInfo{
|
||||
Name: "user1",
|
||||
},
|
||||
Claims: Claims{
|
||||
Username: "user1",
|
||||
TokenType: AccessToken,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "token is successfully issued",
|
||||
fields: fields{
|
||||
name: "kubesphere",
|
||||
secret: []byte("kubesphere"),
|
||||
maximumClockSkew: 0,
|
||||
},
|
||||
args: args{request: &IssueRequest{
|
||||
User: &user.DefaultInfo{
|
||||
Name: "user2",
|
||||
},
|
||||
Claims: Claims{
|
||||
Username: "user2",
|
||||
TokenType: RefreshToken,
|
||||
},
|
||||
ExpiresIn: 0},
|
||||
},
|
||||
want: &VerifiedResponse{
|
||||
User: &user.DefaultInfo{
|
||||
Name: "user2",
|
||||
},
|
||||
Claims: Claims{
|
||||
Username: "user2",
|
||||
TokenType: RefreshToken,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &issuer{
|
||||
name: tt.fields.name,
|
||||
secret: tt.fields.secret,
|
||||
maximumClockSkew: tt.fields.maximumClockSkew,
|
||||
}
|
||||
token, err := s.IssueTo(tt.args.request)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("IssueTo() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
got, err := s.Verify(token)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Verify() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got == nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, got.TokenType, tt.want.TokenType)
|
||||
assert.Equal(t, got.Issuer, tt.fields.name)
|
||||
assert.Equal(t, got.Username, tt.want.Username)
|
||||
assert.Equal(t, got.Subject, tt.want.User.GetName())
|
||||
assert.NotZero(t, got.IssuedAt)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_issuer_Verify(t *testing.T) {
|
||||
type fields struct {
|
||||
name string
|
||||
secret []byte
|
||||
maximumClockSkew time.Duration
|
||||
}
|
||||
type args struct {
|
||||
token string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *VerifiedResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "token validation failed",
|
||||
fields: fields{
|
||||
name: "kubesphere",
|
||||
secret: []byte("kubesphere"),
|
||||
maximumClockSkew: 0,
|
||||
},
|
||||
args: args{token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwidG9rZW5fdHlwZSI6ImFjY2Vzc190b2tlbiIsImV4cCI6MTYzMDY0MDMyMywiaWF0IjoxNjMwNjM2NzIzLCJpc3MiOiJrdWJlc3BoZXJlIiwibmJmIjoxNjMwNjM2NzIzfQ.4ENxyPTIe-BoQfuY5F4Mon5tB3KeV06B4i2JITRlPA8"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "token is successfully verified",
|
||||
fields: fields{
|
||||
name: "kubesphere",
|
||||
secret: []byte("kubesphere"),
|
||||
maximumClockSkew: 0,
|
||||
},
|
||||
args: args{token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2MzA2MzczOTgsImlzcyI6Imt1YmVzcGhlcmUiLCJzdWIiOiJ1c2VyMiIsInRva2VuX3R5cGUiOiJyZWZyZXNoX3Rva2VuIiwidXNlcm5hbWUiOiJ1c2VyMiJ9.vqPczw4SyytVOQmgaK9ip2dvg2fSQStUUE_Y7Ts45WY"},
|
||||
want: &VerifiedResponse{
|
||||
User: &user.DefaultInfo{
|
||||
Name: "user2",
|
||||
},
|
||||
Claims: Claims{
|
||||
Username: "user2",
|
||||
TokenType: RefreshToken,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &issuer{
|
||||
name: tt.fields.name,
|
||||
secret: tt.fields.secret,
|
||||
maximumClockSkew: tt.fields.maximumClockSkew,
|
||||
}
|
||||
got, err := s.Verify(tt.args.token)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Verify() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got == nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, got.TokenType, tt.want.TokenType)
|
||||
assert.Equal(t, got.Issuer, tt.fields.name)
|
||||
assert.Equal(t, got.Username, tt.want.Username)
|
||||
assert.Equal(t, got.Subject, tt.want.User.GetName())
|
||||
assert.NotZero(t, got.IssuedAt)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_issuer_keyFunc(t *testing.T) {
|
||||
type fields struct {
|
||||
name string
|
||||
secret []byte
|
||||
maximumClockSkew time.Duration
|
||||
}
|
||||
type args struct {
|
||||
token *jwt.Token
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
}{
|
||||
{
|
||||
name: "sign key obtained successfully",
|
||||
fields: fields{
|
||||
secret: []byte("kubesphere"),
|
||||
},
|
||||
args: args{token: &jwt.Token{
|
||||
Method: jwt.SigningMethodHS256,
|
||||
Header: map[string]interface{}{"alg": "HS256"},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "sign key obtained successfully",
|
||||
fields: fields{},
|
||||
args: args{token: &jwt.Token{
|
||||
Method: jwt.SigningMethodRS256,
|
||||
Header: map[string]interface{}{"alg": "RS256"},
|
||||
}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s, err := NewIssuer(authentication.NewOptions())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
iss := s.(*issuer)
|
||||
got, err := iss.keyFunc(tt.args.token)
|
||||
assert.NotNil(t, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user