github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/ctl/auth/simple_test.go (about)

     1  // Copyright (c) 2017 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package auth
    22  
    23  import (
    24  	"bytes"
    25  	"context"
    26  	"fmt"
    27  	"net/http"
    28  	"net/http/httptest"
    29  	"testing"
    30  
    31  	"github.com/stretchr/testify/require"
    32  	yaml "gopkg.in/yaml.v2"
    33  )
    34  
    35  var (
    36  	testUser               = "testUser"
    37  	testOriginator         = "testOriginator"
    38  	testUserIDHeader       = "testUserIDHeader"
    39  	testOriginatorIDHeader = "testOriginatorIDHeader"
    40  	testConfig             = SimpleAuthConfig{
    41  		Authentication: authenticationConfig{
    42  			UserIDHeader: "testHeader",
    43  		},
    44  		Authorization: authorizationConfig{
    45  			ReadWhitelistEnabled:    true,
    46  			WriteWhitelistEnabled:   false,
    47  			ReadWhitelistedUserIDs:  []string{testUser},
    48  			WriteWhitelistedUserIDs: []string{},
    49  		},
    50  	}
    51  	testConfigWithOriginatorID = SimpleAuthConfig{
    52  		Authentication: authenticationConfig{
    53  			UserIDHeader:       testUserIDHeader,
    54  			OriginatorIDHeader: testOriginatorIDHeader,
    55  		},
    56  		Authorization: authorizationConfig{
    57  			ReadWhitelistEnabled:    true,
    58  			WriteWhitelistEnabled:   true,
    59  			ReadWhitelistedUserIDs:  []string{},
    60  			WriteWhitelistedUserIDs: []string{testUser},
    61  		},
    62  	}
    63  )
    64  
    65  func TestSimpleAuthConfigUnmarshal(t *testing.T) {
    66  	configStr := `
    67  authentication:
    68    userIDHeader: user-id
    69  authorization:
    70    readWhitelistEnabled: true
    71    readWhitelistedUserIDs:
    72      - foo
    73      - bar
    74    writeWhitelistEnabled: true
    75    writeWhitelistedUserIDs:
    76      - bar
    77      - baz
    78  `
    79  	var cfg SimpleAuthConfig
    80  	require.NoError(t, yaml.Unmarshal([]byte(configStr), &cfg))
    81  	require.Equal(t, "user-id", cfg.Authentication.UserIDHeader)
    82  	require.True(t, cfg.Authorization.ReadWhitelistEnabled)
    83  	require.Equal(t, []string{"foo", "bar"}, cfg.Authorization.ReadWhitelistedUserIDs)
    84  	require.True(t, cfg.Authorization.WriteWhitelistEnabled)
    85  	require.Equal(t, []string{"bar", "baz"}, cfg.Authorization.WriteWhitelistedUserIDs)
    86  }
    87  
    88  func TestNewSimpleAuth(t *testing.T) {
    89  	an := testConfig.NewSimpleAuth().(simpleAuth).authentication
    90  	az := testConfig.NewSimpleAuth().(simpleAuth).authorization
    91  	require.Equal(t, an.userIDHeader, "testHeader")
    92  	require.Equal(t, az.readWhitelistEnabled, true)
    93  	require.Equal(t, az.writeWhitelistEnabled, false)
    94  	require.Equal(t, az.readWhitelistedUserIDs, []string{"testUser"})
    95  	require.Equal(t, az.writeWhitelistedUserIDs, []string{})
    96  }
    97  
    98  func TestSetUser(t *testing.T) {
    99  	a := testConfig.NewSimpleAuth()
   100  	ctx := context.Background()
   101  	require.Nil(t, ctx.Value(UserIDField))
   102  	ctx = a.SetUser(ctx, "foo")
   103  	require.Equal(t, "foo", ctx.Value(UserIDField).(string))
   104  }
   105  
   106  func TestGetUser(t *testing.T) {
   107  	a := testConfig.NewSimpleAuth()
   108  	ctx := context.Background()
   109  
   110  	id, err := a.GetUser(ctx)
   111  	require.Empty(t, id)
   112  	require.Error(t, err)
   113  
   114  	ctx = a.SetUser(ctx, "foo")
   115  	id, err = a.GetUser(ctx)
   116  	require.Equal(t, "foo", id)
   117  	require.NoError(t, err)
   118  }
   119  
   120  func TestSimpleAuthenticationAuthenticate(t *testing.T) {
   121  	authentication := simpleAuthentication{
   122  		userIDHeader: "foo",
   123  	}
   124  
   125  	require.Nil(t, authentication.authenticate("bar"))
   126  	require.EqualError(t, authentication.authenticate(""), "must provide header: [foo]")
   127  }
   128  
   129  func TestSimpleAuthorizationAuthorize(t *testing.T) {
   130  	authorization := simpleAuthorization{
   131  		readWhitelistEnabled:    true,
   132  		writeWhitelistEnabled:   false,
   133  		readWhitelistedUserIDs:  []string{"foo", "bar"},
   134  		writeWhitelistedUserIDs: []string{"foo", "bar", "baz"},
   135  	}
   136  
   137  	require.Nil(t, authorization.authorize(ReadOnlyAuthorization, "foo"))
   138  	require.Nil(t, authorization.authorize(WriteOnlyAuthorization, "foo"))
   139  	require.Nil(t, authorization.authorize(NoAuthorization, "foo"))
   140  	require.Nil(t, authorization.authorize(WriteOnlyAuthorization, "baz"))
   141  	require.EqualError(t, authorization.authorize(ReadOnlyAuthorization, "baz"), "supplied userID: [baz] is not authorized")
   142  	require.EqualError(t, authorization.authorize(ReadWriteAuthorization, "baz"), "supplied userID: [baz] is not authorized")
   143  	require.EqualError(t, authorization.authorize(AuthorizationType(100), "baz"), "unsupported authorization type 100 passed to handler")
   144  }
   145  
   146  func TestAuthorizeUserForAccess(t *testing.T) {
   147  	userID := "user2"
   148  	whitelistedUserIDs := []string{"user1", "user2", "user3"}
   149  	require.NoError(t, authorizeUserForAccess(userID, whitelistedUserIDs, false))
   150  	require.NoError(t, authorizeUserForAccess(userID, whitelistedUserIDs, true))
   151  }
   152  
   153  func TestAuthorizeUserForAccessUserNotWhitelisted(t *testing.T) {
   154  	userID := "user4"
   155  	whitelistedUserIDs := []string{"user1", "user2", "user3"}
   156  	require.NoError(t, authorizeUserForAccess(userID, whitelistedUserIDs, false))
   157  	require.EqualError(
   158  		t,
   159  		authorizeUserForAccess(userID, whitelistedUserIDs, true),
   160  		fmt.Sprintf("supplied userID: [%s] is not authorized", userID),
   161  	)
   162  }
   163  
   164  func TestHealthCheck(t *testing.T) {
   165  	a := testConfig.NewSimpleAuth()
   166  	f := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   167  		v, err := a.GetUser(r.Context())
   168  		require.NoError(t, err)
   169  		require.Equal(t, "testHeader", v)
   170  	})
   171  
   172  	wrappedCall := a.NewAuthHandler(NoAuthorization, f, writeAPIResponse)
   173  	wrappedCall.ServeHTTP(httptest.NewRecorder(), &http.Request{})
   174  }
   175  
   176  func TestAuthenticateFailure(t *testing.T) {
   177  	a := testConfig.NewSimpleAuth()
   178  	f := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   179  		v, err := a.GetUser(r.Context())
   180  		require.NoError(t, err)
   181  		require.Equal(t, "testHeader", v)
   182  	})
   183  	recorder := httptest.NewRecorder()
   184  
   185  	wrappedCall := a.NewAuthHandler(NoAuthorization, f, writeAPIResponse)
   186  	wrappedCall.ServeHTTP(recorder, &http.Request{})
   187  	require.Equal(t, http.StatusUnauthorized, recorder.Code)
   188  	require.Equal(t, "application/json", recorder.HeaderMap["Content-Type"][0])
   189  }
   190  
   191  func TestAuthenticateWithOriginatorID(t *testing.T) {
   192  	req, err := http.NewRequest(http.MethodPost, "/update", nil)
   193  	require.NoError(t, err)
   194  	req.Header.Add(testUserIDHeader, testUser)
   195  	req.Header.Add(testOriginatorIDHeader, testOriginator)
   196  
   197  	a := testConfigWithOriginatorID.NewSimpleAuth()
   198  	f := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   199  		v, err := a.GetUser(r.Context())
   200  		require.NoError(t, err)
   201  		require.Equal(t, testOriginator, v)
   202  		writeAPIResponse(w, http.StatusOK, "success!")
   203  	})
   204  	recorder := httptest.NewRecorder()
   205  	wrappedCall := a.NewAuthHandler(NoAuthorization, f, writeAPIResponse)
   206  	wrappedCall.ServeHTTP(recorder, req)
   207  	require.Equal(t, http.StatusOK, recorder.Code)
   208  	require.Equal(t, "application/json", recorder.HeaderMap["Content-Type"][0])
   209  }
   210  
   211  func TestAuthorizeFailure(t *testing.T) {
   212  	a := testConfig.NewSimpleAuth()
   213  	f := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   214  		v, err := a.GetUser(r.Context())
   215  		require.NoError(t, err)
   216  		require.Equal(t, "testHeader", v)
   217  	})
   218  	recorder := httptest.NewRecorder()
   219  	req, err := http.NewRequest("Get", "/create", bytes.NewBuffer(nil))
   220  	require.NoError(t, err)
   221  	req.Header.Add("testHeader", "validUserID")
   222  
   223  	wrappedCall := a.NewAuthHandler(ReadOnlyAuthorization, f, writeAPIResponse)
   224  	wrappedCall.ServeHTTP(recorder, req)
   225  	require.Equal(t, http.StatusForbidden, recorder.Code)
   226  	require.Equal(t, "application/json", recorder.HeaderMap["Content-Type"][0])
   227  }
   228  
   229  func writeAPIResponse(w http.ResponseWriter, code int, msg string) error {
   230  	w.Header().Set("Content-Type", "application/json")
   231  	w.WriteHeader(code)
   232  	_, err := w.Write([]byte(msg))
   233  
   234  	return err
   235  }