cyberark/secretless-broker

View on GitHub
internal/plugin/connectors/http/generic/oauth/v1/protocol_test.go

Summary

Maintainability
A
0 mins
Test Coverage
package oauth1protocol

import (
    "fmt"
    "io"
    "io/ioutil"
    gohttp "net/http"
    "net/url"
    "strings"
    "testing"

    "github.com/stretchr/testify/assert"
)

func Test_extractOAuthParams(t *testing.T) {
    paramMap := map[string]string{
        consumerKey:          "consumerKey",
        consumerSecret:       "consumerSecret",
        token:                "token",
        tokenSecret:          "tokenSecret",
        oauthNonce:           "oauth_nonce",
        oauthSignatureMethod: "oauth_signature_method",
        oauthVersion:         "oauth_version",
        oauthTimestamp:       "oauth_timestamp",
    }
    result := extractOAuthParams(paramMap)
    // Nonce and TimeStamp are generated at runtime and unknown
    result.TimeStamp = ""
    result.Nonce = ""

    want := OAuth1{
        ConsumerKey:     "consumerKey",
        ConsumerSecret:  "consumerSecret",
        Nonce:           "",
        SignatureMethod: "HMAC-SHA1",
        TimeStamp:       "",
        Token:           "token",
        TokenSecret:     "tokenSecret",
        Version:         "1.0",
    }
    assert.Equal(t, want, result)
}

func createRequestBody(s string) io.ReadCloser {
    return ioutil.NopCloser(strings.NewReader(s))
}

func Test_collectParameters(t *testing.T) {
    type args struct {
        oauth1 OAuth1
        r      *gohttp.Request
    }

    params := args{
        oauth1: OAuth1{
            ConsumerKey:     "xvz1evFS4wEEPTGEFPHBog",
            Nonce:           "kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg",
            SignatureMethod: "HMAC-SHA1",
            TimeStamp:       "1318622958",
            Token:           "370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb",
            Version:         "1.0",
        },
        r: &gohttp.Request{
            Method: "POST",
            URL: &url.URL{
                Scheme:   "http",
                Host:     "example.com",
                Path:     "/wp-json/wp/v2/posts",
                RawQuery: "include_entities=true",
            },
            Header: map[string][]string{
                "Authorization": {"doesn't matter"},
                "Content-Type":  {"application/x-www-form-urlencoded"},
            },
            Body: createRequestBody("status=Hello%20Foo%20%2B%20Bar%2C%20a%20signed%20OAuth%20request%21"),
        },
    }

    result := collectParameters(params.oauth1, params.r)

    want := map[string][]string{
        "include_entities":       {"true"},
        "oauth_consumer_key":     {"xvz1evFS4wEEPTGEFPHBog"},
        "oauth_nonce":            {"kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg"},
        "oauth_signature_method": {"HMAC-SHA1"},
        "oauth_timestamp":        {"1318622958"},
        "oauth_token":            {"370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb"},
        "oauth_version":          {"1.0"},
        "status":                 {"Hello%20Foo%20%2B%20Bar%2C%20a%20signed%20OAuth%20request%21"},
    }

    assert.Equal(t, want, result)
}

func Test_generateParameterString(t *testing.T) {
    type args struct {
        paramMap map[string][]string
    }
    tests := []struct {
        description string
        args        args
        want        string
        wantErr     bool
    }{
        {
            description: "keys with single values",
            args: args{
                paramMap: map[string][]string{
                    "oauth_consumer_key":     {"key"},
                    "oauth_nonce":            {"nonce"},
                    "oauth_signature_method": {"HMAC-SHA1"},
                    "oauth_timestamp":        {"123456789"},
                    "oauth_token":            {"token"},
                    "oauth_version":          {"1.0"},
                },
            },
            want: "oauth_consumer_key=key&oauth_nonce=nonce&oauth_signature_method=HMAC-SHA1&oauth_timestamp=123456789&oauth_token=token&oauth_version=1.0",
        },
        {
            description: "key with multiple values",
            args: args{
                paramMap: map[string][]string{
                    "test_multi_var[]": {"multi1", "multi2"},
                    "test_single_var":  {"single"},
                },
            },
            want: "test_multi_var[]=multi1&test_multi_var[]=multi2&test_single_var=single",
        },
        {
            description: "sorts map values",
            args: args{
                paramMap: map[string][]string{
                    "c": {"3"},
                    "a": {"1"},
                    "b": {"2"},
                },
            },
            want: "a=1&b=2&c=3",
        },
    }
    for _, tt := range tests {
        t.Run(tt.description, func(t *testing.T) {
            result := generateParameterString(tt.args.paramMap)
            assert.Equal(t, tt.want, result)
        },
        )
    }
}

func Test_generateBaseString(t *testing.T) {
    type args struct {
        paramString string
        r           *gohttp.Request
    }
    tests := []struct {
        description string
        args        args
        want        string
    }{
        {
            description: "all components supplied",
            args: args{
                paramString: "include_entities=true&oauth_consumer_key=xvz1evFS4wEEPTGEFPHBog&oauth_nonce=kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1318622958&oauth_token=370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb&oauth_version=1.0&status=Hello%20Foo%20%2B%20Bar%2C%20a%20signed%20OAuth%20request%21",
                r: &gohttp.Request{
                    Method: "POST",
                    URL: &url.URL{
                        Scheme: "https",
                        Host:   "api.twitter.com",
                        Path:   "/1.1/statuses/update.json",
                    },
                },
            },
            want: "POST&https%3A%2F%2Fapi.twitter.com%2F1.1%2Fstatuses%2Fupdate.json&include_entities%3Dtrue%26oauth_consumer_key%3Dxvz1evFS4wEEPTGEFPHBog%26oauth_nonce%3DkYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1318622958%26oauth_token%3D370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb%26oauth_version%3D1.0%26status%3DHello%2520Foo%2520%252B%2520Bar%252C%2520a%2520signed%2520OAuth%2520request%2521",
        },
        {
            description: "no components supplied",
            args: args{
                paramString: "include_entities=true&oauth_consumer_key=xvz1evFS4wEEPTGEFPHBog&oauth_nonce=kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1318622958&oauth_token=370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb&oauth_version=1.0&status=Hello%20Foo%20%2B%20Bar%2C%20a%20signed%20OAuth%20request%21",
                r: &gohttp.Request{
                    Method: "",
                    URL: &url.URL{
                        Scheme: "",
                        Host:   "",
                        Path:   "",
                    },
                },
            },
            want: "&%3A%2F%2F&include_entities%3Dtrue%26oauth_consumer_key%3Dxvz1evFS4wEEPTGEFPHBog%26oauth_nonce%3DkYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1318622958%26oauth_token%3D370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb%26oauth_version%3D1.0%26status%3DHello%2520Foo%2520%252B%2520Bar%252C%2520a%2520signed%2520OAuth%2520request%2521",
        },
    }
    for _, tt := range tests {
        t.Run(tt.description, func(t *testing.T) {
            result := generateBaseString(tt.args.r, tt.args.paramString)
            assert.Equal(t, tt.want, result)
        })
    }
}

func Test_generateNonce(t *testing.T) {
    type args struct {
        length int
    }
    tests := []struct {
        description string
        args        args
        want        int
    }{
        {
            description: "Generates a 16 length nonce",
            args: args{
                length: 16,
            },
            want: 16,
        },
        {
            description: "Generates a 32 length nonce",
            args: args{
                length: 32,
            },
            want: 32,
        },
    }
    for _, tt := range tests {
        t.Run(tt.description, func(t *testing.T) {
            result := len(generateNonce(tt.args.length, nonceCharset))
            assert.True(t, result == tt.args.length)
        })
    }
}

func Test_generateSigningKey(t *testing.T) {
    type args struct {
        oauth1 OAuth1
    }
    tests := []struct {
        description string
        args        args
        want        string
        wantErr     bool
    }{
        {
            description: "both keys",
            args: args{
                OAuth1{
                    ConsumerSecret: "kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw",
                    TokenSecret:    "LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE",
                },
            },
            want: "kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw&LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE",
        },
        {
            description: "no token secret",
            args: args{
                OAuth1{
                    ConsumerSecret: "kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw",
                    TokenSecret:    "",
                },
            },
            want: "kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw&",
        },
        {
            description: "both empty",
            args: args{
                OAuth1{
                    ConsumerSecret: "",
                    TokenSecret:    "",
                },
            },
            want: "&",
        },
    }
    for _, tt := range tests {
        t.Run(tt.description, func(t *testing.T) {
            result := generateSigningKey(tt.args.oauth1)
            assert.Equal(t, tt.want, result)
        })
    }
}

func Test_constructOAuthString(t *testing.T) {
    t.Run("creates expected header", func(t *testing.T) {
        oauth1 := OAuth1{
            ConsumerKey:     "xvz1evFS4wEEPTGEFPHBog",
            ConsumerSecret:  "kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw",
            Nonce:           "kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg",
            Signature:       "tnnArxj06cWHq44gCs1OSKk%2FjLY%3D",
            SignatureMethod: "HMAC-SHA1",
            TimeStamp:       "1318622958",
            Token:           "370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb",
            TokenSecret:     "LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE",
            Version:         "1.0",
        }

        result, err := constructOAuthString(oauth1)

        want := "OAuth oauth_consumer_key=\"xvz1evFS4wEEPTGEFPHBog\", oauth_nonce=\"kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg\", oauth_signature=\"tnnArxj06cWHq44gCs1OSKk%2FjLY%3D\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"1318622958\", oauth_token=\"370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb\", oauth_version=\"1.0\""

        assert.NoError(t, err)
        assert.Equal(t, want, result)
    })
}

func Test_encodeURI(t *testing.T) {
    type args struct {
        stringToEncode string
    }
    tests := []struct {
        description string
        args        args
        want        string
    }{
        {
            description: "snowman emoji",
            args: args{
                stringToEncode: "☃",
            },
            want: "%E2%98%83",
        },
        {
            description: "ALPHA",
            args: args{
                stringToEncode: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
            },
            want: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
        },
        {
            description: "DIGIT",
            args: args{
                stringToEncode: "1234567890",
            },
            want: "1234567890",
        },
        {
            description: "special characters not to encode",
            args: args{
                stringToEncode: "-._~",
            },
            want: "-._~",
        },
        {
            description: "special characters to encode",
            args: args{
                stringToEncode: "`!@#$%^&*()+=[{]}\\|;:'\",<>/? ",
            },
            want: "%60%21%40%23%24%25%5E%26%2A%28%29%2B%3D%5B%7B%5D%7D%5C%7C%3B%3A%27%22%2C%3C%3E%2F%3F%20",
        },
    }
    for _, tt := range tests {
        t.Run(tt.description, func(t *testing.T) {
            result := encodeURI(tt.args.stringToEncode)
            assert.Equal(t, tt.want, result)
        })
    }
}

func Test_checkRequiredOAuthParams(t *testing.T) {
    type args struct {
        paramMap map[string]string
    }
    tests := []struct {
        description string
        args        args
        want        error
        wantErr     bool
    }{
        {
            description: "all values present: no error",
            args: args{
                paramMap: map[string]string{
                    "consumer_key":    "conKey",
                    "consumer_secret": "conSecret",
                    "token":           "apiToken",
                    "token_secret":    "tokSecret",
                },
            },
            want: nil,
        },
        {
            description: "missing value: consumer_key",
            args: args{
                paramMap: map[string]string{
                    "consumer_secret": "conSecret",
                    "token":           "apiToken",
                    "token_secret":    "tokSecret",
                },
            },
            want: fmt.Errorf("required oAuth1 parameter 'consumer_key' not found"),
        },
    }
    for _, tt := range tests {
        t.Run(tt.description, func(t *testing.T) {
            result := checkRequiredOAuthParams(tt.args.paramMap)
            assert.Equal(t, tt.want, result)
        },
        )
    }
}

func Test_CreateOAuth1Header(t *testing.T) {
    testCases := []struct {
        description string
        params      map[string]string
        expErrStr   string
    }{
        {
            description: "all values present: no error",
            params: map[string]string{
                "consumer_key":    "conKey",
                "consumer_secret": "conSecret",
                "token":           "apiToken",
                "token_secret":    "tokSecret",
            },
        },
        {
            description: "missing value: consumer_key",
            params: map[string]string{
                "consumer_secret": "conSecret",
                "token":           "apiToken",
                "token_secret":    "tokSecret",
            },
            expErrStr: "required oAuth1 parameter 'consumer_key' not found",
        },
    }

    for _, tc := range testCases {
        t.Run(tc.description, func(t *testing.T) {
            req := &gohttp.Request{
                Method: "POST",
                URL: &url.URL{
                    Scheme:   "http",
                    Host:     "example.com",
                    Path:     "/wp-json/wp/v2/posts",
                    RawQuery: "include_entities=true",
                },
                Header: map[string][]string{
                    "Authorization": {"doesn't matter"},
                    "Content-Type":  {"application/x-www-form-urlencoded"},
                },
                Body: createRequestBody("status=Hello%20Foo%20%2B%20Bar%2C%20a%20signed%20OAuth%20request%21"),
            }
            res, err := CreateOAuth1Header(tc.params, req)

            if tc.expErrStr != "" {
                assert.Error(t, err)
                assert.EqualError(t, err, tc.expErrStr)
                return
            }

            assert.NoError(t, err)
            assert.Contains(t, res, "OAuth oauth_consumer_key=\"conKey\", oauth_nonce=\"")
            assert.Contains(t, res, "oauth_signature=\"")
            assert.Contains(t, res, "oauth_signature_method=\"")
            assert.Contains(t, res, "oauth_timestamp=\"")
            assert.Contains(t, res, "oauth_token=\"apiToken\", oauth_version=\"1.0\"")
        })
    }
}