github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/model/remote/remote_test.go (about)

     1  package remote
     2  
     3  import (
     4  	"bytes"
     5  	"net/http"
     6  	"net/http/httptest"
     7  	"net/url"
     8  	"testing"
     9  
    10  	"github.com/cozy/cozy-stack/pkg/config/config"
    11  	"github.com/cozy/cozy-stack/pkg/consts"
    12  	"github.com/cozy/cozy-stack/pkg/couchdb"
    13  	"github.com/cozy/cozy-stack/pkg/prefixer"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  const doctype = "org.example.request"
    19  
    20  func TestParseRawRequest(t *testing.T) {
    21  	config.UseTestFile(t)
    22  
    23  	raw := `GET`
    24  	_, err := ParseRawRequest(doctype, raw)
    25  	assert.Equal(t, ErrInvalidRequest, err)
    26  
    27  	raw = `PUT https://example.org/`
    28  	_, err = ParseRawRequest(doctype, raw)
    29  	assert.Equal(t, ErrInvalidRequest, err)
    30  
    31  	raw = `GET ftp://example.org/`
    32  	_, err = ParseRawRequest(doctype, raw)
    33  	assert.Equal(t, ErrInvalidRequest, err)
    34  
    35  	raw = `GET /etc/hosts`
    36  	_, err = ParseRawRequest(doctype, raw)
    37  	assert.Equal(t, ErrInvalidRequest, err)
    38  
    39  	raw = `GET https://example.org/
    40  Foo`
    41  	_, err = ParseRawRequest(doctype, raw)
    42  	assert.Equal(t, ErrInvalidRequest, err)
    43  
    44  	// Allow a trailing \n after headers on GET
    45  	raw = `GET https://www.wikidata.org/wiki/Special:EntityData/{{entity}}.json
    46  Content-Type: application/json
    47  `
    48  	_, err = ParseRawRequest(doctype, raw)
    49  	assert.NoError(t, err)
    50  
    51  	raw = `GET https://www.wikidata.org/wiki/Special:EntityData/{{entity}}.json`
    52  	r1, err := ParseRawRequest(doctype, raw)
    53  	assert.NoError(t, err)
    54  	assert.Equal(t, "GET", r1.Verb)
    55  	assert.Equal(t, "https", r1.URL.Scheme)
    56  	assert.Equal(t, "www.wikidata.org", r1.URL.Host)
    57  	assert.Equal(t, "/wiki/Special:EntityData/{{entity}}.json", r1.URL.Path)
    58  	assert.Equal(t, "", r1.URL.RawQuery)
    59  
    60  	raw = `POST https://www.wikidata.org/w/api.php?action=wbsearchentities&search={{q}}&language=en&format=json
    61  Accept-Language: fr-FR,en
    62  Content-Type: application/json
    63  
    64  one={{one}}
    65  two={{two}}`
    66  	r2, err := ParseRawRequest(doctype, raw)
    67  	assert.NoError(t, err)
    68  	assert.Equal(t, "POST", r2.Verb)
    69  	assert.Equal(t, "https", r2.URL.Scheme)
    70  	assert.Equal(t, "www.wikidata.org", r2.URL.Host)
    71  	assert.Equal(t, "/w/api.php", r2.URL.Path)
    72  	assert.Equal(t, "action=wbsearchentities&search={{q}}&language=en&format=json", r2.URL.RawQuery)
    73  	assert.Equal(t, "fr-FR,en", r2.Headers["Accept-Language"])
    74  	assert.Equal(t, "application/json", r2.Headers["Content-Type"])
    75  	assert.Equal(t, `one={{one}}
    76  two={{two}}`, r2.Body)
    77  }
    78  
    79  func TestExtractVariablesGET(t *testing.T) {
    80  	config.UseTestFile(t)
    81  
    82  	u, err := url.Parse("https://example.org/foo?one=un&two=deux")
    83  	assert.NoError(t, err)
    84  	in := &http.Request{URL: u}
    85  	vars, err := extractVariables("GET", in)
    86  	assert.NoError(t, err)
    87  	assert.Equal(t, "un", vars["one"])
    88  	assert.Equal(t, "deux", vars["two"])
    89  }
    90  
    91  func TestExtractVariablesPOST(t *testing.T) {
    92  	config.UseTestFile(t)
    93  
    94  	body := bytes.NewReader([]byte(`{"one": "un", "two": "deux"}`))
    95  	in := httptest.NewRequest("POST", "https://example.com/bar", body)
    96  	vars, err := extractVariables("POST", in)
    97  	assert.NoError(t, err)
    98  	assert.Equal(t, "un", vars["one"])
    99  	assert.Equal(t, "deux", vars["two"])
   100  
   101  	body = bytes.NewReader([]byte(`one=un&two=deux`))
   102  	in = httptest.NewRequest("POST", "https://example.com/bar", body)
   103  	_, err = extractVariables("POST", in)
   104  	assert.Error(t, err)
   105  }
   106  
   107  func TestInjectVariables(t *testing.T) {
   108  	config.UseTestFile(t)
   109  
   110  	raw := `POST https://example.org/foo/{{bar}}?q={{q}}
   111  Content-Type: {{contentType}}
   112  Accept-Language: {{lang}},en
   113  
   114  { "one": "{{ json one }}", "two": "{{ json two }}" }
   115  <p>{{html content}}</p>`
   116  	r, err := ParseRawRequest(doctype, raw)
   117  	require.NoError(t, err)
   118  
   119  	vars := map[string]string{
   120  		"contentType": "application/json",
   121  		"bar":         "baz&/",
   122  		"q":           "Q42&?",
   123  		"lang":        "fr-FR\n",
   124  		"one":         "un\"\n",
   125  		"two":         "deux",
   126  		"content":     "hey ! <<>>",
   127  	}
   128  
   129  	err = injectVariables(r, vars)
   130  	assert.NoError(t, err)
   131  	assert.Equal(t, "POST", r.Verb)
   132  	assert.Equal(t, "https", r.URL.Scheme)
   133  	assert.Equal(t, "example.org", r.URL.Host)
   134  	assert.Equal(t, "/foo/baz&%2F", r.URL.Path)
   135  	assert.Equal(t, "q=Q42%26%3F", r.URL.RawQuery)
   136  	assert.Equal(t, "fr-FR\\n,en", r.Headers["Accept-Language"])
   137  	assert.Equal(t, "application/json", r.Headers["Content-Type"])
   138  	assert.Equal(t, `{ "one": "un\"\n", "two": "deux" }
   139  <p>hey ! &lt;&lt;&gt;&gt;</p>`, r.Body)
   140  
   141  	r, err = ParseRawRequest(doctype, `POST https://example.org/{{missing}}`)
   142  	assert.NoError(t, err)
   143  	err = injectVariables(r, vars)
   144  	assert.Equal(t, ErrMissingVar, err)
   145  }
   146  
   147  func TestInjectSecret(t *testing.T) {
   148  	if testing.Short() {
   149  		t.Skip("a couchdb is required for this test: test skipped due to the use of --short flag")
   150  	}
   151  
   152  	config.UseTestFile(t)
   153  
   154  	doctype := "cc.cozycloud.foobar"
   155  	doc := couchdb.JSONDoc{
   156  		Type: consts.RemoteSecrets,
   157  		M: map[string]interface{}{
   158  			"_id":   doctype,
   159  			"token": "123456789",
   160  		},
   161  	}
   162  	err := couchdb.CreateNamedDocWithDB(prefixer.SecretsPrefixer, &doc)
   163  	assert.NoError(t, err)
   164  	defer func() {
   165  		_ = couchdb.DeleteDoc(prefixer.SecretsPrefixer, &doc)
   166  	}()
   167  
   168  	raw := `POST https://example.org/foo/
   169  Authorization: Bearer {{secret_token}}
   170  
   171  { "bar": "baz" }
   172  `
   173  	r, err := ParseRawRequest(doctype, raw)
   174  	assert.NoError(t, err)
   175  	err = injectVariables(r, map[string]string{})
   176  	assert.NoError(t, err)
   177  	assert.Equal(t, "Bearer 123456789", r.Headers["Authorization"])
   178  }