go.temporal.io/server@v1.23.0/common/authorization/default_authorizer_test.go (about)

     1  // The MIT License
     2  //
     3  // Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
     4  //
     5  // Copyright (c) 2020 Uber Technologies, Inc.
     6  //
     7  // Permission is hereby granted, free of charge, to any person obtaining a copy
     8  // of this software and associated documentation files (the "Software"), to deal
     9  // in the Software without restriction, including without limitation the rights
    10  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    11  // copies of the Software, and to permit persons to whom the Software is
    12  // furnished to do so, subject to the following conditions:
    13  //
    14  // The above copyright notice and this permission notice shall be included in
    15  // all copies or substantial portions of the Software.
    16  //
    17  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    18  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    19  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    20  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    21  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    22  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    23  // THE SOFTWARE.
    24  
    25  package authorization
    26  
    27  import (
    28  	"context"
    29  	"reflect"
    30  	"testing"
    31  
    32  	"github.com/golang/mock/gomock"
    33  	"github.com/stretchr/testify/require"
    34  	"github.com/stretchr/testify/suite"
    35  	"go.temporal.io/server/common/config"
    36  )
    37  
    38  var (
    39  	claimsNone           = Claims{}
    40  	claimsNamespaceAdmin = Claims{
    41  		Namespaces: map[string]Role{
    42  			testNamespace: RoleAdmin,
    43  		},
    44  	}
    45  	claimsNamespaceWriter = Claims{
    46  		Namespaces: map[string]Role{
    47  			testNamespace: RoleWriter,
    48  		},
    49  	}
    50  	claimsNamespaceReader = Claims{
    51  		Namespaces: map[string]Role{
    52  			testNamespace: RoleReader,
    53  		},
    54  	}
    55  	claimsBarAdmin = Claims{
    56  		Namespaces: map[string]Role{
    57  			"bar": RoleAdmin,
    58  		},
    59  	}
    60  	claimsSystemAdmin = Claims{
    61  		System: RoleAdmin,
    62  	}
    63  	claimsSystemWriter = Claims{
    64  		System: RoleWriter,
    65  	}
    66  	claimsSystemReader = Claims{
    67  		System: RoleReader,
    68  	}
    69  	targetNamespaceWriteBar = CallTarget{
    70  		APIName:   "/temporal.api.workflowservice.v1.WorkflowService/RespondWorkflowTaskCompleted",
    71  		Namespace: "bar",
    72  	}
    73  	targetNamespaceWriteBAR = CallTarget{
    74  		APIName:   "/temporal.api.workflowservice.v1.WorkflowService/RespondWorkflowTaskCompleted",
    75  		Namespace: "BAR",
    76  	}
    77  	targetOperatorNamespaceRead = CallTarget{
    78  		APIName:   "/temporal.api.operatorservice.v1.OperatorService/ListSearchAttributes",
    79  		Namespace: testNamespace,
    80  	}
    81  	targetGrpcHealthCheck = CallTarget{
    82  		APIName:   "/grpc.health.v1.Health/Check",
    83  		Namespace: "",
    84  	}
    85  	targetGetSystemInfo = CallTarget{
    86  		APIName:   "/temporal.api.workflowservice.v1.WorkflowService/GetSystemInfo",
    87  		Namespace: "",
    88  	}
    89  	targetStartWorkflow = CallTarget{
    90  		APIName:   "/temporal.api.workflowservice.v1.WorkflowService/StartWorkflowExecution",
    91  		Namespace: testNamespace,
    92  	}
    93  	targetAdminAPI = CallTarget{
    94  		APIName:   "/temporal.server.api.adminservice.v1.AdminService/AddSearchAttributes",
    95  		Namespace: testNamespace,
    96  	}
    97  )
    98  
    99  type (
   100  	defaultAuthorizerSuite struct {
   101  		suite.Suite
   102  		*require.Assertions
   103  
   104  		controller *gomock.Controller
   105  		authorizer Authorizer
   106  	}
   107  )
   108  
   109  func TestDefaultAuthorizerSuite(t *testing.T) {
   110  	s := new(defaultAuthorizerSuite)
   111  	suite.Run(t, s)
   112  }
   113  
   114  func (s *defaultAuthorizerSuite) SetupTest() {
   115  	s.Assertions = require.New(s.T())
   116  	s.controller = gomock.NewController(s.T())
   117  	s.authorizer = NewDefaultAuthorizer()
   118  }
   119  
   120  func (s *defaultAuthorizerSuite) TearDownTest() {
   121  	s.controller.Finish()
   122  }
   123  
   124  func (s *defaultAuthorizerSuite) TestAuthorize() {
   125  	testCases := []struct {
   126  		Name     string
   127  		Claims   Claims
   128  		Target   CallTarget
   129  		Decision Decision
   130  	}{
   131  		// SystemAdmin is allowed on everything
   132  		{"SystemAdminOnFooBar", claimsSystemAdmin, targetNamespaceWriteBar, DecisionAllow},
   133  		{"SystemAdminOnAdminAPI", claimsSystemAdmin, targetAdminAPI, DecisionAllow},
   134  		{"SystemAdminOnStartWorkflow", claimsSystemAdmin, targetStartWorkflow, DecisionAllow},
   135  
   136  		// SystemWriter is allowed on all read only APIs and non-admin APIs on every namespaces
   137  		{"SystemWriterOnFooBar", claimsSystemWriter, targetNamespaceWriteBar, DecisionAllow},
   138  		{"SystemWriterOnAdminAPI", claimsSystemWriter, targetAdminAPI, DecisionDeny},
   139  		{"SystemWriterOnStartWorkflow", claimsSystemWriter, targetStartWorkflow, DecisionAllow},
   140  
   141  		// SystemReader is allowed on all read only APIs and blocked
   142  		{"SystemReaderOnFooBar", claimsSystemReader, targetNamespaceWriteBar, DecisionDeny},
   143  		{"SystemReaderOnAdminAPI", claimsSystemReader, targetAdminAPI, DecisionDeny},
   144  		{"SystemReaderOnStartWorkflow", claimsSystemReader, targetStartWorkflow, DecisionDeny},
   145  
   146  		// NamespaceAdmin is allowed on admin service to their own namespaces (test-namespace)
   147  		{"NamespaceAdminOnAdminAPI", claimsNamespaceAdmin, targetAdminAPI, DecisionDeny},
   148  		{"NamespaceAdminOnStartWorkflow", claimsNamespaceAdmin, targetStartWorkflow, DecisionAllow},
   149  		{"NamespaceAdminOnFooBar", claimsNamespaceAdmin, targetNamespaceWriteBar, DecisionDeny}, // namespace mismatch
   150  
   151  		{"BarAdminOnFooBar", claimsBarAdmin, targetNamespaceWriteBar, DecisionAllow},
   152  		{"BarAdminOnFooBAR", claimsBarAdmin, targetNamespaceWriteBAR, DecisionDeny}, // namespace case mismatch
   153  
   154  		// NamespaceWriter is not allowed on admin APIs
   155  		{"NamespaceWriterOnAdminAPI", claimsNamespaceWriter, targetAdminAPI, DecisionDeny},
   156  		{"NamespaceWriterOnStartWorkflow", claimsNamespaceWriter, targetStartWorkflow, DecisionAllow},
   157  		{"NamespaceWriterOnOperatorNamespaceRead", claimsNamespaceWriter, targetOperatorNamespaceRead, DecisionAllow},
   158  		{"NamespaceWriterOnFooBar", claimsNamespaceWriter, targetNamespaceWriteBar, DecisionDeny}, // namespace mismatch
   159  
   160  		// NamespaceReader is allowed on read-only APIs on non admin service
   161  		{"NamespaceReaderOnAdminAPI", claimsNamespaceReader, targetAdminAPI, DecisionDeny},
   162  		{"NamespaceReaderOnStartWorkflow", claimsNamespaceReader, targetStartWorkflow, DecisionDeny},
   163  		{"NamespaceReaderOnFooBar", claimsNamespaceReader, targetNamespaceWriteBar, DecisionDeny}, // namespace mismatch
   164  		{"NamespaceReaderOnListWorkflow", claimsNamespaceReader, targetGetSystemInfo, DecisionAllow},
   165  		{"NamespaceReaderOnOperatorNamespaceRead", claimsNamespaceReader, targetOperatorNamespaceRead, DecisionAllow},
   166  
   167  		// healthcheck allowed to everyone
   168  		{"RoleNoneOnGetSystemInfo", claimsNone, targetGetSystemInfo, DecisionAllow},
   169  		{"NamespaceReaderOnGetSystemInfo", claimsNamespaceReader, targetGetSystemInfo, DecisionAllow},
   170  		{"RoleNoneOnHealthCheck", claimsNone, targetGrpcHealthCheck, DecisionAllow},
   171  		{"NamespaceReaderOnHealthCheck", claimsNamespaceReader, targetGrpcHealthCheck, DecisionAllow},
   172  	}
   173  
   174  	for _, tt := range testCases {
   175  		result, err := s.authorizer.Authorize(context.TODO(), &tt.Claims, &tt.Target)
   176  		s.NoError(err)
   177  		s.Equal(tt.Decision, result.Decision, "Failed case: %v", tt.Name)
   178  	}
   179  }
   180  
   181  func (s *defaultAuthorizerSuite) TestGetAuthorizerFromConfigNoop() {
   182  	s.testGetAuthorizerFromConfig("", true, reflect.TypeOf(&noopAuthorizer{}))
   183  }
   184  func (s *defaultAuthorizerSuite) TestGetAuthorizerFromConfigDefault() {
   185  	s.testGetAuthorizerFromConfig("default", true, reflect.TypeOf(&defaultAuthorizer{}))
   186  }
   187  func (s *defaultAuthorizerSuite) TestGetAuthorizerFromConfigUnknown() {
   188  	s.testGetAuthorizerFromConfig("foo", false, nil)
   189  }
   190  
   191  func (s *defaultAuthorizerSuite) testGetAuthorizerFromConfig(name string, valid bool, authorizerType reflect.Type) {
   192  
   193  	cfg := config.Authorization{Authorizer: name}
   194  	auth, err := GetAuthorizerFromConfig(&cfg)
   195  	if valid {
   196  		s.NoError(err)
   197  		s.NotNil(auth)
   198  		t := reflect.TypeOf(auth)
   199  		s.True(t == authorizerType)
   200  	} else {
   201  		s.Error(err)
   202  		s.Nil(auth)
   203  	}
   204  }