github.com/mendersoftware/go-lib-micro@v0.0.0-20240304135804-e8e39c59b148/identity/token_test.go (about)

     1  // Copyright 2023 Northern.tech AS
     2  //
     3  //	Licensed under the Apache License, Version 2.0 (the "License");
     4  //	you may not use this file except in compliance with the License.
     5  //	You may obtain a copy of the License at
     6  //
     7  //	    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  //	Unless required by applicable law or agreed to in writing, software
    10  //	distributed under the License is distributed on an "AS IS" BASIS,
    11  //	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  //	See the License for the specific language governing permissions and
    13  //	limitations under the License.
    14  package identity
    15  
    16  import (
    17  	"encoding/base64"
    18  	"encoding/json"
    19  	"net/http"
    20  	"testing"
    21  
    22  	"github.com/stretchr/testify/assert"
    23  )
    24  
    25  func boolPtr(val bool) *bool {
    26  	return &val
    27  }
    28  
    29  func makeClaimsFull(sub, tenant, plan string, device, user, trial bool) string {
    30  	claim := struct {
    31  		Subject string `json:"sub,omitempty"`
    32  		Tenant  string `json:"mender.tenant,omitempty"`
    33  		Device  *bool  `json:"mender.device,omitempty"`
    34  		User    *bool  `json:"mender.user,omitempty"`
    35  		Plan    string `json:"mender.plan,omitempty"`
    36  		Trial   bool   `json:"mender.trial"`
    37  	}{
    38  		Subject: sub,
    39  		Tenant:  tenant,
    40  		Plan:    plan,
    41  		Trial:   trial,
    42  	}
    43  
    44  	if device {
    45  		claim.Device = boolPtr(true)
    46  	}
    47  	if user {
    48  		claim.User = boolPtr(true)
    49  	}
    50  	data, _ := json.Marshal(&claim)
    51  	rawclaim := base64.RawURLEncoding.EncodeToString(data)
    52  	return rawclaim
    53  }
    54  
    55  func makeClaimsPart(sub, tenant, plan string) string {
    56  	return makeClaimsFull(sub, tenant, plan, false, false, false)
    57  }
    58  
    59  func TestExtractIdentity(t *testing.T) {
    60  	_, err := ExtractIdentity("foo")
    61  	assert.Error(t, err)
    62  
    63  	_, err = ExtractIdentity("foo.bar")
    64  	assert.Error(t, err)
    65  
    66  	_, err = ExtractIdentity("foo.bar.baz")
    67  	assert.Error(t, err)
    68  
    69  	// should fail, token is malformed, missing header & signature
    70  	rawclaims := makeClaimsPart("foobar", "", "")
    71  	_, err = ExtractIdentity(rawclaims)
    72  	assert.Error(t, err)
    73  
    74  	// correct case
    75  	idata, err := ExtractIdentity("foo." + rawclaims + ".bar")
    76  	assert.NoError(t, err)
    77  	assert.Equal(t, Identity{Subject: "foobar"}, idata)
    78  
    79  	// missing subject
    80  	enc := base64.RawURLEncoding.EncodeToString([]byte(`{"iss": "Mender"}`))
    81  	_, err = ExtractIdentity("foo." + enc + ".bar")
    82  	assert.Error(t, err)
    83  
    84  	// bad subject
    85  	enc = base64.RawURLEncoding.EncodeToString([]byte(`{"sub": 1}`))
    86  	_, err = ExtractIdentity("foo." + enc + ".bar")
    87  	assert.Error(t, err)
    88  
    89  	enc = base64.RawURLEncoding.EncodeToString([]byte(`{"sub": "123", "mender.device": true}`))
    90  	idata, err = ExtractIdentity("foo." + enc + ".bar")
    91  	assert.NoError(t, err)
    92  	assert.Equal(t, Identity{Subject: "123", IsDevice: true}, idata)
    93  
    94  	enc = base64.RawURLEncoding.EncodeToString([]byte(`{"sub": "123", "mender.user": true}`))
    95  	idata, err = ExtractIdentity("foo." + enc + ".bar")
    96  	assert.NoError(t, err)
    97  	assert.Equal(t, Identity{Subject: "123", IsUser: true}, idata)
    98  
    99  	enc = base64.RawURLEncoding.EncodeToString([]byte(`{"sub": "123", "mender.user": {"garbage": 2}}`))
   100  	_, err = ExtractIdentity("foo." + enc + ".bar")
   101  	assert.Error(t, err)
   102  	assert.Contains(t, err.Error(), "failed to decode JSON JWT claims")
   103  
   104  	_, err = ExtractIdentity("foo.barrr.baz")
   105  	assert.Error(t, err)
   106  	assert.Contains(t, err.Error(), "failed to decode base64 JWT claims")
   107  
   108  	rawclaims = makeClaimsFull("foobar", "", "", false, true, true)
   109  	idata, err = ExtractIdentity("foo." + rawclaims + ".bar")
   110  	assert.NoError(t, err)
   111  	assert.Equal(t, Identity{Subject: "foobar", IsUser: true, Trial: true}, idata)
   112  }
   113  
   114  func TestExtractIdentityFromHeaders(t *testing.T) {
   115  	r := &http.Request{
   116  		Header: http.Header{},
   117  	}
   118  	_, err := ExtractJWTFromHeader(r)
   119  	assert.Error(t, err)
   120  
   121  	r.Header.Set("Authorization", "Basic foobar")
   122  	_, err = ExtractJWTFromHeader(r)
   123  	assert.Error(t, err)
   124  
   125  	r.Header.Set("Authorization", "Bearer")
   126  	_, err = ExtractJWTFromHeader(r)
   127  	assert.Error(t, err)
   128  
   129  	// correct cate
   130  	rawclaims := makeClaimsPart("foobar", "", "")
   131  	actualJWT := "foo." + rawclaims + ".bar"
   132  	r.Header.Set("Authorization", "Bearer "+actualJWT)
   133  	jwt, err := ExtractJWTFromHeader(r)
   134  	assert.NoError(t, err)
   135  	assert.Equal(t, actualJWT, jwt)
   136  
   137  	r.Header.Del("Authorization")
   138  	r.AddCookie(&http.Cookie{
   139  		Name:  "JWT",
   140  		Value: "foo." + rawclaims + ".bar",
   141  	})
   142  	jwt, err = ExtractJWTFromHeader(r)
   143  	assert.NoError(t, err)
   144  	assert.Equal(t, actualJWT, jwt)
   145  }