306 lines
8.5 KiB
Go
306 lines
8.5 KiB
Go
package tmobtokengen
|
|
|
|
import (
|
|
_ "embed"
|
|
"log"
|
|
"os"
|
|
"reflect"
|
|
"runtime"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/go-jose/go-jose/v4"
|
|
)
|
|
|
|
var mockPrivateKey any
|
|
|
|
//go:embed pkcs8_test.key
|
|
var mockPrivateKeyStr []byte
|
|
|
|
func mockUuidFunc() string {
|
|
return "b04b038e-52f0-b7d0-95f9-1cb04475f2ab"
|
|
}
|
|
|
|
var mockClientSecret = "dGVzdDp0ZXN0" // test:test
|
|
|
|
var mockEhtsMapEmptyBody = EHTSMap{}.
|
|
SetAuthorization("Basic " + mockClientSecret).
|
|
SetURI("/oauth2/v6/tokens").
|
|
SetHTTPMethod("POST")
|
|
|
|
var mockEhtsMapNonEmptyBody = EHTSMap{}.
|
|
SetAuthorization("Basic " + mockClientSecret).
|
|
SetURI("/iotcp/v1/line-of-service/devices/summary").
|
|
SetHTTPMethod("POST").
|
|
SetContentType("application/json").
|
|
SetBody(`{"modifiedSince": "2021-02-17T00:00:00+00:00", "accountId" : "12342"}`)
|
|
|
|
var mockTime = time.Unix(1656322028, 179000000).In(time.UTC)
|
|
|
|
var mockSigner jose.Signer
|
|
|
|
func TestMain(m *testing.M) {
|
|
var err error
|
|
|
|
//let's set mock private key
|
|
mockPrivateKey, err = ParsePrivateKey(mockPrivateKeyStr, nil)
|
|
if err != nil {
|
|
log.Printf("error parsing mockPrivateKey: %v", err)
|
|
|
|
os.Exit(1)
|
|
}
|
|
|
|
opts := new(jose.SignerOptions)
|
|
opts.WithType("JWT")
|
|
|
|
//let's set mock signer
|
|
mockSigner, err = jose.NewSigner(jose.SigningKey{Algorithm: jose.RS256, Key: mockPrivateKey}, opts)
|
|
if err != nil {
|
|
log.Printf("error creating mockSigner: %v", err)
|
|
|
|
os.Exit(1)
|
|
}
|
|
|
|
m.Run()
|
|
}
|
|
|
|
func TestGenerateUUID(t *testing.T) {
|
|
got1 := GenerateUUID()
|
|
got2 := GenerateUUID()
|
|
if got1 == "" || got2 == "" || got1 == got2 {
|
|
t.Errorf("GenerateUUD() something very weird is going on: got1 = %v, got2 = %v", got1, got2)
|
|
}
|
|
}
|
|
|
|
func TestNewTokenGenerator(t *testing.T) {
|
|
b, err := os.ReadFile("./pkcs8_test.key")
|
|
if err != nil {
|
|
t.Fatalf("Missing test file to load %v", err)
|
|
}
|
|
tg, err := NewTokenGenerator("test", "test", time.Minute, string(b))
|
|
if err != nil {
|
|
t.Fatalf("NewTokenGenerator() error = %v", err)
|
|
}
|
|
|
|
if tg == nil {
|
|
t.Fatalf("NewTokenGenerator() = %v, want non-nil", tg)
|
|
}
|
|
|
|
if tg.signer == nil {
|
|
t.Fatalf("NewTokenGenerator() signer = %v, want non-nil", tg.signer)
|
|
}
|
|
|
|
opts := tg.signer.Options()
|
|
if opts.ExtraHeaders == nil {
|
|
t.Fatalf("NewTokenGenerator() opts() = %v, want non-nil", opts)
|
|
}
|
|
|
|
if len(opts.ExtraHeaders) != 1 {
|
|
t.Fatalf("NewTokenGenerator() len(opts.ExtraHeaders) = %v, want 1", len(opts.ExtraHeaders))
|
|
}
|
|
|
|
if val, ok := opts.ExtraHeaders["typ"]; !ok || val != jose.ContentType("JWT") {
|
|
t.Fatalf("NewTokenGenerator() signer.Options() typ = %v, want JWT", opts.ExtraHeaders["typ"])
|
|
}
|
|
|
|
if tg.clientSecret != mockClientSecret {
|
|
t.Fatalf("NewTokenGenerator() clientSecret = %v, want %v", tg.clientSecret, mockClientSecret)
|
|
}
|
|
|
|
if tg.expDuration != time.Minute {
|
|
t.Fatalf("NewTokenGenerator() expDuration = %v, want %v", tg.expDuration, time.Minute)
|
|
}
|
|
|
|
f1 := runtime.FuncForPC(reflect.ValueOf(tg.genUuid).Pointer()).Name()
|
|
f2 := runtime.FuncForPC(reflect.ValueOf(GenerateUUID).Pointer()).Name()
|
|
|
|
if f1 != f2 {
|
|
t.Errorf("NewTokenGenerator() genUuid = %s, want %s", f1, f2)
|
|
}
|
|
}
|
|
|
|
func TestParsePrivateKey(t *testing.T) {
|
|
key, err := ParsePrivateKey(mockPrivateKeyStr, nil)
|
|
if err != nil {
|
|
t.Fatalf("ParsePrivateKey() got err = %v, want nil", err)
|
|
}
|
|
|
|
if key == nil {
|
|
t.Fatalf("ParsePrivateKey() got key = %v, want non-nil", key)
|
|
}
|
|
|
|
key, err = ParsePrivateKey([]byte{}, nil)
|
|
if err == nil {
|
|
t.Fatalf("ParsePrivateKey() got err = %v, want non-nil", err)
|
|
}
|
|
|
|
if key != nil {
|
|
t.Fatalf("ParsePrivateKey() got key = %v, want nil", key)
|
|
}
|
|
}
|
|
|
|
func TestPopTokenGenerator_ClientSecretAsAuthVal(t *testing.T) {
|
|
g := &PopTokenGenerator{
|
|
signer: mockSigner,
|
|
clientSecret: mockClientSecret,
|
|
expDuration: time.Minute,
|
|
genUuid: mockUuidFunc,
|
|
}
|
|
|
|
authVal := "Basic " + mockClientSecret
|
|
if got := g.ClientSecretAsAuthVal(); got != authVal {
|
|
t.Errorf("ClientSecretAsAuthVal() got = %v, want = %v", got, authVal)
|
|
}
|
|
}
|
|
|
|
func TestPopTokenGenerator_Generate(t *testing.T) {
|
|
g := &PopTokenGenerator{
|
|
signer: mockSigner,
|
|
clientSecret: mockClientSecret,
|
|
expDuration: time.Minute * 2,
|
|
genUuid: mockUuidFunc,
|
|
}
|
|
|
|
got, err := g.Generate(mockEhtsMapEmptyBody)
|
|
if err != nil {
|
|
t.Fatalf("Generate() error = %v", err)
|
|
}
|
|
|
|
if len(got) == 0 {
|
|
t.Fatalf("Generate() got = %v, want non-empty", got)
|
|
}
|
|
}
|
|
|
|
func TestPopTokenGenerator_buildClaims(t *testing.T) {
|
|
c, err := buildClaims(mockEhtsMapEmptyBody, mockTime, time.Minute, mockUuidFunc)
|
|
if err != nil {
|
|
t.Fatalf("buildClaims() got err = %v, want nil", err)
|
|
}
|
|
builtClaims := "{\"edts\":\"wdFcplwLzHDuO4_OlXLFrvh28DwtgKswIrnsUj0dU0I\",\"v\":\"1\",\"exp\":1656322088,\"ehts\":\"Authorization;uri;http-method\",\"iat\":1656322028,\"jti\":\"b04b038e-52f0-b7d0-95f9-1cb04475f2ab\"}"
|
|
if string(c) != builtClaims {
|
|
t.Errorf("buildClaims() got = %v, want = %v", string(c), builtClaims)
|
|
}
|
|
}
|
|
|
|
func TestPopTokenGenerator_generate(t *testing.T) {
|
|
g := &PopTokenGenerator{
|
|
signer: mockSigner,
|
|
clientSecret: mockClientSecret,
|
|
expDuration: time.Minute * 2,
|
|
genUuid: mockUuidFunc,
|
|
}
|
|
|
|
type args struct {
|
|
ehts EHTSMap
|
|
curTime time.Time
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "success-empty-body",
|
|
args: args{
|
|
ehts: mockEhtsMapEmptyBody,
|
|
curTime: mockTime,
|
|
},
|
|
want: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJlZHRzIjoid2RGY3Bsd0x" +
|
|
"6SER1TzRfT2xYTEZydmgyOER3dGdLc3dJcm5zVWowZFUwSSIsInYiOiIxIiwiZ" +
|
|
"XhwIjoxNjU2MzIyMTQ4LCJlaHRzIjoiQXV0aG9yaXphdGlvbjt1cmk7aHR0cC1" +
|
|
"tZXRob2QiLCJpYXQiOjE2NTYzMjIwMjgsImp0aSI6ImIwNGIwMzhlLTUyZjAtY" +
|
|
"jdkMC05NWY5LTFjYjA0NDc1ZjJhYiJ9.Pe4BLC1LeClMzJ4UdZXN3CVT-eG52i" +
|
|
"60RsGH70RsXquy4rRDV0IxE1f7Wr04nGT9t1YJXG4qBaiX3VDrqvk03f7Acn0Q" +
|
|
"wyRQCItDiUiMHWNAB3FwkAllyJIyuT6l9IQehTWC0YT4Fv0HF0K5XUlt8sIp63" +
|
|
"Lk0HU-iibUzkfN7FSvXovZz1uy4zLD6bbodxFwYs4HOo6tPiVkapLuJlET3mez" +
|
|
"__m8b-qeQzcZ45sNOIL6MQ-UZDB8LNFUJOr4Wdq6ox3QM8owaXoRVf9ffkAFmT" +
|
|
"X4kNg2knz8CLVWMtVzgPOQX7s7qoTVCDucz3Yxx-1hN1HUu1Kgjhau2G-DDq9i" +
|
|
"mA",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "success-with-body",
|
|
args: args{
|
|
ehts: mockEhtsMapNonEmptyBody,
|
|
curTime: mockTime,
|
|
},
|
|
want: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJlZHRzIjoiY2hoa0NWRUt" +
|
|
"ET2FXNi1zXzNHVnBkZnk4UzJ3czBLRlhzRkprdUZzTnhWNCIsInYiOiIxIiwiZ" +
|
|
"XhwIjoxNjU2MzIyMTQ4LCJlaHRzIjoiQXV0aG9yaXphdGlvbjt1cmk7aHR0cC1" +
|
|
"tZXRob2Q7Ym9keTtDb250ZW50LVR5cGUiLCJpYXQiOjE2NTYzMjIwMjgsImp0a" +
|
|
"SI6ImIwNGIwMzhlLTUyZjAtYjdkMC05NWY5LTFjYjA0NDc1ZjJhYiJ9.nz7viG" +
|
|
"O2cqyoGAarHALoIy0FbX2mlG6esweuJk8ZRvw0xmoH7oR1wHdwnkgRB2gar_Fe" +
|
|
"I42Ni2AjWzOYY26siEiJDM0Nv7qbiCC6SZpCq3xYYwN27Ky41m74eqh8wYod-T" +
|
|
"5sN-vLqVDLIewFLQ7EftQ-d8a2VLKO4NyL9F0yHjXOn5LEsAzNRBNDEOYebIHD" +
|
|
"mF4wFRVTm5MJMyYlKj04kDojRb5111FNe68POblY5n1SZyrbSnAE4qLrPrz65I" +
|
|
"lpRnkqln9ORGx62EG8UpAP8RQf_oKZ1ZGbmI3KB1t0lhMW59lUT2mYCJZ9sRaQ" +
|
|
"RO3VKERfNYZtOq6xprhKVl55mQ",
|
|
wantErr: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := g.generate(tt.args.ehts, tt.args.curTime)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("generate() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("generate() got = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_buildClientSecret(t *testing.T) {
|
|
want := mockClientSecret
|
|
if got := buildClientSecret("test", "test"); got != want {
|
|
t.Errorf("buildClientSecret() = %v, want %v", got, want)
|
|
}
|
|
}
|
|
|
|
func Test_sign(t *testing.T) {
|
|
type args struct {
|
|
claims []byte
|
|
signer jose.Signer
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "sign-success",
|
|
args: args{[]byte("claims"), mockSigner},
|
|
want: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.Y2xhaW1z.UpFWT1xyFSNFOi" +
|
|
"vlI83g1187to8V0Mw6Bfz6NIwGJE-n_turYNpLDCEjoAJmAFhYQHb289JLIoLC" +
|
|
"WOET4dbh0Od2mNZODNIZhY_Xu7hlVLu7bPX1Fvl7rC4UiJYVKoZKyc7924pvJP" +
|
|
"ndmKnIwrt_hygkO3GEBCpkxI57_7lNyBXtYVqSGQyayV0Vq55673uC4egdnjNv" +
|
|
"utC6JGsSnY1PekQbT4YyVgqZeTCOI0sKNtEKzNVtgr6qXs7VOYxzAOAH9kjOSK" +
|
|
"TtZ66VSCbq_TF9F08fEx9X_sCGW58K4HT_EeuXUW4EDUgfZFqoPtmzej50wQX9" +
|
|
"IiaaAtJ5CVxMcdf9Pw",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "sign-fail",
|
|
args: args{[]byte("claims"), nil},
|
|
want: "",
|
|
wantErr: true,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := sign(tt.args.signer, tt.args.claims)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("sign() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("sign() got = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|