github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/api_test.go (about)

     1  // Copyright 2016 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package libkb
     5  
     6  import (
     7  	"crypto/tls"
     8  	"net/http"
     9  	"net/url"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/require"
    13  
    14  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    15  )
    16  
    17  func TestIsReddit(t *testing.T) {
    18  	// Test both with and without a subdomain.
    19  	req, _ := http.NewRequest("GET", "http://reddit.com", nil)
    20  	if !isReddit(req) {
    21  		t.Fatal("should be a reddit URL")
    22  	}
    23  	req, _ = http.NewRequest("GET", "http://www.reddit.com", nil)
    24  	if !isReddit(req) {
    25  		t.Fatal("should be a reddit URL")
    26  	}
    27  	// Test a non-reddit URL.
    28  	req, _ = http.NewRequest("GET", "http://github.com", nil)
    29  	if isReddit(req) {
    30  		t.Fatal("should NOT be a reddit URL")
    31  	}
    32  }
    33  
    34  const (
    35  	uriExpected  = "https://api-1.core.keybaseapi.com"
    36  	pingExpected = "https://api-1.core.keybaseapi.com/_/api/1.0/ping.json"
    37  )
    38  
    39  func TestProductionCA(t *testing.T) {
    40  	tc := SetupTest(t, "prod_ca", 1)
    41  	defer tc.Cleanup()
    42  	mctx := NewMetaContextForTest(tc)
    43  
    44  	t.Log("WARNING: setting run mode to production, be careful:")
    45  	tc.G.Env.Test.UseProductionRunMode = true
    46  
    47  	serverURI, err := tc.G.Env.GetServerURI()
    48  	require.NoError(t, err)
    49  
    50  	if serverURI != uriExpected {
    51  		t.Fatalf("production server uri: %s, expected %s", serverURI, uriExpected)
    52  	}
    53  
    54  	err = tc.G.ConfigureAPI()
    55  	require.NoError(t, err)
    56  
    57  	// make sure endpoint is correct:
    58  	arg := APIArg{Endpoint: "ping"}
    59  	internal, ok := tc.G.API.(*InternalAPIEngine)
    60  	if !ok {
    61  		t.Fatal("failed to cast API to internal api engine")
    62  	}
    63  	url := internal.getURL(arg, false)
    64  	if url.String() != pingExpected {
    65  		t.Fatalf("api url: %s, expected %s", url.String(), pingExpected)
    66  	}
    67  
    68  	_, err = tc.G.API.Post(mctx, arg)
    69  	if err != nil {
    70  		t.Fatal(err)
    71  	}
    72  
    73  	_, err = tc.G.API.Get(mctx, arg)
    74  	if err != nil {
    75  		t.Fatal(err)
    76  	}
    77  }
    78  
    79  func TestProductionBadCA(t *testing.T) {
    80  	tc := SetupTest(t, "prod_ca", 1)
    81  	defer tc.Cleanup()
    82  	mctx := NewMetaContextForTest(tc)
    83  
    84  	t.Log("WARNING: setting run mode to production, be careful:")
    85  	tc.G.Env.Test.UseProductionRunMode = true
    86  
    87  	serverURI, err := tc.G.Env.GetServerURI()
    88  	require.NoError(t, err)
    89  
    90  	if serverURI != uriExpected {
    91  		t.Fatalf("production server uri: %s, expected %s", serverURI, uriExpected)
    92  	}
    93  
    94  	// change the api CA to one that api.keybase.io doesn't know:
    95  	apiCAOverrideForTest = unknownCA
    96  	defer func() {
    97  		apiCAOverrideForTest = ""
    98  	}()
    99  
   100  	err = tc.G.ConfigureAPI()
   101  	require.NoError(t, err)
   102  
   103  	// make sure endpoint is correct:
   104  	arg := APIArg{Endpoint: "ping"}
   105  	internal, ok := tc.G.API.(*InternalAPIEngine)
   106  	if !ok {
   107  		t.Fatal("failed to cast API to internal api engine")
   108  	}
   109  	iurl := internal.getURL(arg, false)
   110  	if iurl.String() != pingExpected {
   111  		t.Fatalf("api url: %s, expected %s", iurl.String(), pingExpected)
   112  	}
   113  
   114  	_, err = tc.G.API.Post(mctx, arg)
   115  	if err == nil {
   116  		t.Errorf("api ping POST worked with unknown CA")
   117  	} else {
   118  		checkX509Err(t, err)
   119  	}
   120  
   121  	_, err = tc.G.API.Get(mctx, arg)
   122  	if err == nil {
   123  		t.Errorf("api ping GET worked with unknown CA")
   124  	} else {
   125  		checkX509Err(t, err)
   126  	}
   127  }
   128  
   129  // this error is buried, so dig for it:
   130  func checkX509Err(t *testing.T, err error) {
   131  	if err == nil {
   132  		t.Fatal("isX509Err called with nil error")
   133  	}
   134  
   135  	a, ok := err.(APINetError)
   136  	if !ok {
   137  		t.Errorf("invalid error type: %T, expected libkb.APINetError", err)
   138  		return
   139  	}
   140  
   141  	b, ok := a.Err.(*url.Error)
   142  	if !ok {
   143  		t.Errorf("APINetError err field type: %T, expected *url.Error", a.Err)
   144  		return
   145  	}
   146  
   147  	_, ok = b.Err.(*tls.CertificateVerificationError)
   148  	if !ok {
   149  		t.Errorf("url.Error Err field type: %T, expected x509.UnknownAuthorityError", b.Err)
   150  	}
   151  }
   152  
   153  const unknownCA = `-----BEGIN CERTIFICATE-----
   154  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
   183  sB31+boS8zC7SOmgWuaHeOQdLT8=
   184  -----END CERTIFICATE-----
   185  `
   186  
   187  type DummyConfigReader struct {
   188  	NullConfiguration
   189  }
   190  
   191  var _ ConfigReader = (*DummyConfigReader)(nil)
   192  
   193  func (r *DummyConfigReader) GetDeviceID() keybase1.DeviceID {
   194  	return "dummy-device-id"
   195  }
   196  
   197  type DummyUpdaterConfigReader struct{}
   198  
   199  var _ UpdaterConfigReader = (*DummyUpdaterConfigReader)(nil)
   200  
   201  func (r *DummyUpdaterConfigReader) GetInstallID() InstallID {
   202  	return "dummy-install-id"
   203  }
   204  
   205  func TestInstallIDHeaders(t *testing.T) {
   206  	tc := SetupTest(t, "test", 1)
   207  	defer tc.Cleanup()
   208  	mctx := NewMetaContextForTest(tc)
   209  
   210  	// Hack in the device ID and install ID with dummy readers.
   211  	tc.G.Env.config = &DummyConfigReader{}
   212  	tc.G.Env.updaterConfig = &DummyUpdaterConfigReader{}
   213  
   214  	api, err := NewInternalAPIEngine(tc.G)
   215  	if err != nil {
   216  		t.Fatal(err)
   217  	}
   218  	res, err := api.Get(mctx, APIArg{
   219  		Endpoint:    "pkg/show",
   220  		SessionType: APISessionTypeOPTIONAL,
   221  		Args:        HTTPArgs{},
   222  	})
   223  	if err != nil {
   224  		t.Fatal(err)
   225  	}
   226  
   227  	deviceID, err := res.Body.AtKey("device_id").GetString()
   228  	if err != nil {
   229  		t.Fatal(err)
   230  	}
   231  	if deviceID != "dummy-device-id" {
   232  		t.Fatalf("expected device ID to be reflected back, got %s", res.Body.MarshalPretty())
   233  	}
   234  
   235  	installID, err := res.Body.AtKey("install_id").GetString()
   236  	if err != nil {
   237  		t.Fatal(err)
   238  	}
   239  	if installID != "dummy-install-id" {
   240  		t.Fatalf("expected install ID to be reflected back, got %s", res.Body.MarshalPretty())
   241  	}
   242  }
   243  func TestInstallIDHeadersAnon(t *testing.T) {
   244  	tc := SetupTest(t, "test", 1)
   245  	defer tc.Cleanup()
   246  	mctx := NewMetaContextForTest(tc)
   247  
   248  	// Hack in the device ID and install ID with dummy readers.
   249  	tc.G.Env.config = &DummyConfigReader{}
   250  	tc.G.Env.updaterConfig = &DummyUpdaterConfigReader{}
   251  
   252  	api, err := NewInternalAPIEngine(tc.G)
   253  	if err != nil {
   254  		t.Fatal(err)
   255  	}
   256  	res, err := api.Get(mctx, APIArg{
   257  		Endpoint:    "pkg/show",
   258  		SessionType: APISessionTypeNONE,
   259  		Args:        HTTPArgs{},
   260  	})
   261  	if err != nil {
   262  		t.Fatal(err)
   263  	}
   264  
   265  	_, err = res.Body.AtKey("device_id").GetString()
   266  	if err == nil {
   267  		t.Fatal("Device ID should not be here")
   268  	}
   269  
   270  	_, err = res.Body.AtKey("install_id").GetString()
   271  	if err == nil {
   272  		t.Fatal("Install ID should not be here")
   273  	}
   274  }