Initial cloud-services repo - gateway service + pkg modules
This commit is contained in:
118
pkg/tmobtokengen/ehts.go
Normal file
118
pkg/tmobtokengen/ehts.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package tmobtokengen
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type EhtsKey string
|
||||
|
||||
const (
|
||||
Authorization EhtsKey = "Authorization"
|
||||
URI EhtsKey = "uri"
|
||||
HTTPMethod EhtsKey = "http-method"
|
||||
ContentType EhtsKey = "Content-Type"
|
||||
B2BClient EhtsKey = "B2b-client" // No idea what this is for
|
||||
Body EhtsKey = "body"
|
||||
)
|
||||
|
||||
// ehtsKeyList order is important for serialization.
|
||||
var ehtsKeyList = []EhtsKey{Authorization, URI, HTTPMethod, Body, ContentType, B2BClient}
|
||||
|
||||
func ehtsToString(ehts map[EhtsKey]string) (ks string, vs string) {
|
||||
kb, vb := new(strings.Builder), new(strings.Builder)
|
||||
for _, k := range ehtsKeyList {
|
||||
v, ok := ehts[k]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if kb.Len() > 0 {
|
||||
kb.WriteRune(';')
|
||||
}
|
||||
|
||||
kb.WriteString(string(k))
|
||||
vb.WriteString(v)
|
||||
}
|
||||
|
||||
return kb.String(), vb.String()
|
||||
}
|
||||
|
||||
func b64EncodeEHTS(ehts []byte) []byte {
|
||||
sum := sha256.Sum256(ehts)
|
||||
encoded := make([]byte, base64.URLEncoding.EncodedLen(len(sum)))
|
||||
base64.URLEncoding.Encode(encoded, sum[:])
|
||||
|
||||
// It appears to be that T-Mob removes the padding.
|
||||
i := len(encoded) - 1
|
||||
for ; i > 0; i-- {
|
||||
if i != '=' {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return encoded[:i]
|
||||
}
|
||||
|
||||
// EHTSMap is a map of EHTS keys and values.
|
||||
// Set[X] methods are used to set values in the map,
|
||||
// Yet if the value for the given setter is empty, it won't set anything.
|
||||
// Thus remember to set explicitly the value to empty string if there is need.
|
||||
type EHTSMap map[EhtsKey]string
|
||||
|
||||
func (m EHTSMap) Copy() EHTSMap {
|
||||
c := make(EHTSMap)
|
||||
for k, v := range m {
|
||||
c[k] = v
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
func (m EHTSMap) SetContentType(contentType string) EHTSMap {
|
||||
if len(contentType) > 0 {
|
||||
m[ContentType] = contentType
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m EHTSMap) SetAuthorization(authorization string) EHTSMap {
|
||||
if len(authorization) > 0 {
|
||||
m[Authorization] = authorization
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m EHTSMap) SetB2BClient(b2bClient string) EHTSMap {
|
||||
if len(b2bClient) > 0 {
|
||||
m[B2BClient] = b2bClient
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m EHTSMap) SetURI(uri string) EHTSMap {
|
||||
if len(uri) > 0 {
|
||||
m[URI] = uri
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m EHTSMap) SetBody(body string) EHTSMap {
|
||||
if len(body) > 0 {
|
||||
m[Body] = body
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m EHTSMap) SetHTTPMethod(httpMethod string) EHTSMap {
|
||||
if len(httpMethod) > 0 {
|
||||
m[HTTPMethod] = httpMethod
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
101
pkg/tmobtokengen/ehts_test.go
Normal file
101
pkg/tmobtokengen/ehts_test.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package tmobtokengen
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEHTSMap_SettersNonEmpty(t *testing.T) {
|
||||
m := EHTSMap{}
|
||||
setters := []struct {
|
||||
key EhtsKey
|
||||
setter func(string) EHTSMap
|
||||
}{
|
||||
{key: ContentType, setter: m.SetContentType},
|
||||
{key: Authorization, setter: m.SetAuthorization},
|
||||
{key: B2BClient, setter: m.SetB2BClient},
|
||||
{key: Body, setter: m.SetBody},
|
||||
{key: HTTPMethod, setter: m.SetHTTPMethod},
|
||||
{key: URI, setter: m.SetURI},
|
||||
}
|
||||
|
||||
for _, s := range setters {
|
||||
rs := GenerateUUID()
|
||||
m = s.setter(rs)
|
||||
if m[s.key] != rs {
|
||||
t.Errorf("set%s() = %v, want %v", s.key, m[s.key], rs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEHTSMap_SettersEmpty(t *testing.T) {
|
||||
m := EHTSMap{}
|
||||
setters := []struct {
|
||||
key EhtsKey
|
||||
setter func(string) EHTSMap
|
||||
}{
|
||||
{key: ContentType, setter: m.SetContentType},
|
||||
{key: Authorization, setter: m.SetAuthorization},
|
||||
{key: B2BClient, setter: m.SetB2BClient},
|
||||
{key: Body, setter: m.SetBody},
|
||||
{key: HTTPMethod, setter: m.SetHTTPMethod},
|
||||
{key: URI, setter: m.SetURI},
|
||||
}
|
||||
|
||||
for _, s := range setters {
|
||||
m = s.setter("")
|
||||
if _, ok := m[s.key]; ok {
|
||||
t.Errorf("ehtsMap[%s] = %v, want no value set", s.key, m[s.key])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ehtsToString(t *testing.T) {
|
||||
type args struct {
|
||||
ehts map[EhtsKey]string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantks string
|
||||
wantvs string
|
||||
}{{
|
||||
name: "empty",
|
||||
args: args{ehts: map[EhtsKey]string{}},
|
||||
wantks: "",
|
||||
wantvs: "",
|
||||
}, {
|
||||
name: "one",
|
||||
args: args{ehts: map[EhtsKey]string{ContentType: "value1"}},
|
||||
wantks: string(ContentType),
|
||||
wantvs: "value1",
|
||||
}, {
|
||||
name: "two",
|
||||
args: args{ehts: map[EhtsKey]string{Authorization: "value1", ContentType: "value2"}},
|
||||
wantks: string(Authorization) + ";" + string(ContentType),
|
||||
wantvs: "value1value2"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if gotks, gotvs := ehtsToString(tt.args.ehts); gotks != tt.wantks {
|
||||
t.Errorf("ehtsToString() = %v, want %v", gotks, tt.wantks)
|
||||
} else if gotvs != tt.wantvs {
|
||||
t.Errorf("ehtsToString() = %v, want %v", gotvs, tt.wantvs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_encodedEhts(t *testing.T) {
|
||||
wantEncoded := []byte("47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU")
|
||||
gotEncoded := b64EncodeEHTS(nil)
|
||||
if !reflect.DeepEqual(gotEncoded, wantEncoded) {
|
||||
t.Errorf("empty-case: b64EncodeEHTS() = %v, want %v", string(gotEncoded), string(wantEncoded))
|
||||
}
|
||||
|
||||
wantEncoded = []byte("ungWv48Bz-pBQUDeXa4iI7ADYaOWF3qctBD_YfIAFa0")
|
||||
gotEncoded = b64EncodeEHTS([]byte("abc"))
|
||||
if !reflect.DeepEqual(gotEncoded, wantEncoded) {
|
||||
t.Errorf("non-empty-case: b64EncodeEHTS() = %v, want %v", string(gotEncoded), string(wantEncoded))
|
||||
}
|
||||
}
|
||||
176
pkg/tmobtokengen/encryption.go
Normal file
176
pkg/tmobtokengen/encryption.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package tmobtokengen
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-jose/go-jose/v4"
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/youmark/pkcs8"
|
||||
)
|
||||
|
||||
func buildClientSecret(clientID, secret string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(clientID + ":" + secret))
|
||||
}
|
||||
|
||||
var (
|
||||
ErrFailedToDecodePem = errors.New("failed to decode privateKey")
|
||||
ErrNilSigner = errors.New("signer cannot be nil")
|
||||
)
|
||||
|
||||
func ParsePrivateKey(pemFileBytes []byte, pwd []byte) (any, error) {
|
||||
block, _ := pem.Decode(pemFileBytes)
|
||||
if block == nil {
|
||||
return nil, ErrFailedToDecodePem
|
||||
}
|
||||
key, _, err := pkcs8.ParsePrivateKey(block.Bytes, pwd)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse pkcs8 private key")
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
type jwtClaims struct {
|
||||
EDTS string `json:"edts,omitempty"`
|
||||
V string `json:"v,omitempty"`
|
||||
Exp int64 `json:"exp,omitempty"`
|
||||
EHTS string `json:"ehts,omitempty"`
|
||||
IAT int64 `json:"iat,omitempty"`
|
||||
UniqueStr string `json:"jti,omitempty"`
|
||||
}
|
||||
|
||||
func buildClaims(
|
||||
ehts map[EhtsKey]string,
|
||||
curTime time.Time,
|
||||
expDuration time.Duration,
|
||||
uuidFunc func() string,
|
||||
) ([]byte, error) {
|
||||
curTime = curTime.UTC()
|
||||
ehtsKeys, ehtsValues := ehtsToString(ehts)
|
||||
encoded := b64EncodeEHTS([]byte(ehtsValues))
|
||||
expTime := curTime.Add(expDuration)
|
||||
uniqStr := uuidFunc()
|
||||
|
||||
objClaims := jwtClaims{
|
||||
EDTS: string(encoded),
|
||||
V: "1",
|
||||
Exp: expTime.Unix(),
|
||||
EHTS: ehtsKeys,
|
||||
IAT: curTime.Unix(),
|
||||
UniqueStr: uniqStr,
|
||||
}
|
||||
|
||||
c, err := json.Marshal(objClaims)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal claims during build")
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func sign(signer jose.Signer, claims []byte) (string, error) {
|
||||
if signer == nil {
|
||||
return "", ErrNilSigner
|
||||
}
|
||||
|
||||
signature, err := signer.Sign(claims)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to sign claims during pop token build")
|
||||
}
|
||||
|
||||
signed, err := signature.CompactSerialize()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to sign claims during pop token build")
|
||||
}
|
||||
|
||||
return signed, nil
|
||||
}
|
||||
|
||||
type Generator interface {
|
||||
Generate(ehts EHTSMap) (string, error)
|
||||
ClientSecretAsAuthVal() string
|
||||
}
|
||||
|
||||
type PopTokenGenerator struct {
|
||||
signer jose.Signer
|
||||
clientSecret string // client secret composed from client id and secret via base64.StdEncoding
|
||||
expDuration time.Duration // token expiration duration
|
||||
genUuid func() string // uniq string is set as generator field for testing purposes
|
||||
}
|
||||
|
||||
func GenerateUUID() string {
|
||||
return uuid.New().String()
|
||||
}
|
||||
|
||||
// NewTokenGenerator creates a new PopTokenGenerator.
|
||||
func NewTokenGenerator(
|
||||
clientID string,
|
||||
secret string,
|
||||
expDuration time.Duration,
|
||||
pkValue string,
|
||||
pkPwd ...[]byte,
|
||||
) (*PopTokenGenerator, error) {
|
||||
// Removing the read from file. We read our key from the environment
|
||||
pkValue = strings.ReplaceAll(pkValue, "\\n", "\n")
|
||||
keyBytes := pkValue
|
||||
|
||||
var pwd []byte
|
||||
if len(pkPwd) != 0 {
|
||||
pwd = pkPwd[0]
|
||||
}
|
||||
|
||||
pk, err := ParsePrivateKey([]byte(keyBytes), pwd)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse private key file")
|
||||
}
|
||||
|
||||
opts := new(jose.SignerOptions)
|
||||
opts.WithType("JWT")
|
||||
signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.RS256, Key: pk}, opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create signer")
|
||||
}
|
||||
|
||||
return &PopTokenGenerator{
|
||||
signer: signer,
|
||||
clientSecret: buildClientSecret(clientID, secret),
|
||||
expDuration: expDuration,
|
||||
genUuid: GenerateUUID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *PopTokenGenerator) ClientSecretAsAuthVal() string {
|
||||
return "Basic " + g.clientSecret
|
||||
}
|
||||
|
||||
// Generate generates a pop token.
|
||||
// It expects EHTSMap to be populated with the following keys, order is changed intrinsically, so don't worry:
|
||||
// - ContentType
|
||||
// - Authorization
|
||||
// - URI
|
||||
// - HTTPMethod
|
||||
// - Body
|
||||
func (g *PopTokenGenerator) Generate(ehts EHTSMap) (string, error) {
|
||||
return g.generate(ehts, time.Now())
|
||||
}
|
||||
|
||||
// generate generates a pop token.
|
||||
// It takes the current time as parameter for testing.
|
||||
func (g *PopTokenGenerator) generate(ehts EHTSMap, curTime time.Time) (string, error) {
|
||||
c, err := buildClaims(ehts, curTime, g.expDuration, g.genUuid)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to build claims")
|
||||
}
|
||||
|
||||
signed, err := sign(g.signer, c)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to sign claims")
|
||||
}
|
||||
|
||||
return signed, nil
|
||||
}
|
||||
305
pkg/tmobtokengen/encryption_test.go
Normal file
305
pkg/tmobtokengen/encryption_test.go
Normal file
@@ -0,0 +1,305 @@
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
17
pkg/tmobtokengen/mock.go
Normal file
17
pkg/tmobtokengen/mock.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package tmobtokengen
|
||||
|
||||
type NewMockTokenGenerator struct {
|
||||
}
|
||||
|
||||
// ClientSecretAsAuthVal implements Generator.
|
||||
func (NewMockTokenGenerator) ClientSecretAsAuthVal() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Generate implements Generator.
|
||||
func (NewMockTokenGenerator) Generate(ehts EHTSMap) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// An easy way to check that that a struct implements and interface before build time
|
||||
var _ Generator = NewMockTokenGenerator{}
|
||||
28
pkg/tmobtokengen/pkcs8_test.key
Normal file
28
pkg/tmobtokengen/pkcs8_test.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDBQ0Wrn3iBE+3y
|
||||
sTGr+vxSX+wtq4hz1W9i6LqjV3O4DeA9hZ8Lj3PbFyLuIYXvlFsb6xXydCSHFg2j
|
||||
b8x1g3sUlh7+hDMh2ryVCDFJ05npCyVoxD05Ya9VUsHw1mjKWUt9+x2/sPYjDVzs
|
||||
zhqEwryacrkzmJlpCCpRnfmnfzL9PBPwr1tSkovPvzlzd+MC86Zu/4t7ZNR+UvFT
|
||||
I/S7SLnTIRFaHV0lUf6XYut3HNUotIVX9qFNaF+OenEMk85dgYGam/vReW0xwkx5
|
||||
QDMk/kE38lw9dB4DqhZGWnPw7NciH8+COOJ+JTmDls9WHCXiP9Fh/9ToHBmdD3LX
|
||||
o2KvrMMPAgMBAAECggEBAII9GIVoyWeLC66idMvmLwZAOEQqtaEB87dfCO+sroIZ
|
||||
b8Vl9+FtgfDibZq2orDqdF+jFD44wKj8VqKOY+XJfjdIV4jDhEXLR4zTYYvT+oOP
|
||||
DF3G6U9zIhpI1AO+Kg47EOHMSab11VmX1siKuFpBdaJLr70ymCes5f/siuKymKUI
|
||||
HMgz10exE9ypa0GPUzY1gtoIGRv2xsVoEy7wn29sJkMhhx4MMtfhtnSaLXjoKByW
|
||||
twOew3rNP4BajdmtGIQe2Z3qz/3dG1LG1jLe5AVoCPKZo5JlrzzjaRDbgv1ZZoze
|
||||
Ddi/RVqF0We4pPCNOdHSjhoWs61xDwdpCy24kUEooHECgYEA3002/N2zNQKgerjx
|
||||
3lkX8GbmHx42n1Q/2ihjiygR8RklAIgFCCfJNpAlHqKLGe4ZW7+llIWQOdA7NHOR
|
||||
DWS8StVlog7FwrgBA7dNh2zcmGVmEtP0vg29mhMBMM5IQ66Nsu+vXXhU4qy0CdWY
|
||||
BeadiHTX7YYA/0NsoMfcfIK0iNcCgYEA3ZACBqSiD8dnNnFVj66ut/zZRHX9bSO6
|
||||
dZ07htdOp9pttZlDzlOvA0QhH+qFxe+6h4NJ07Sf+Opu2PeRh90qYvmz0i2DKQHD
|
||||
CmmpaP6iITvvgOa48/sr7XG1k8stlNLa4cBRG6f35/qwr7ZIcU0N7lvnZYTSguXc
|
||||
+oohOatTuIkCgYEAgvYiHcNYauqTe+Yj1CekZpWyuOVbW65plGTDnMVvYFtC3EDp
|
||||
0pKi66E2Y/UoZ5jAvpJzZdu/bmi1kFmG5LgDxk/JP3YyfbS0w50plxc9eRNe/gPZ
|
||||
Me2VGVu0Aw+4ShmBeUQhMUx1XEu1e18Nvcg28+SzDtbcltjQSKtuoId3ohkCgYEA
|
||||
n2CjBHJTHbSb2z7lhGjsx+77v1J8zZCA5XAITPP+YaIvfw1UCEyMPXY5ucKzHfJX
|
||||
pHldlwt8c8r3l91mc2w1vGLQ5qTUj5/z1D6znZJlwDBkFb5iVydbrv831au3CzIu
|
||||
P2xfK9zE6Ludc5hVPiNmnQrBRnaoE38UWakZQ2mp3LkCgYAO9IZijrLWGTX77W3l
|
||||
ruIj2IHKEtb+27aFdZjoMfBpU+HOoWacnmBL3vL0gq8J7KwWuJa18cDLIaiIggHc
|
||||
fFjuF3lTk62dLro94yGr01rIRQnhtoBW5evutX85ukUQQo7E0ieABZP7V1Fqjvg+
|
||||
2oVGWBo1Wzar6CDovkH0yapPTA==
|
||||
-----END PRIVATE KEY-----
|
||||
Reference in New Issue
Block a user