storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/bucket/policy/policy_test.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2018 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package policy
    18  
    19  import (
    20  	"encoding/json"
    21  	"net"
    22  	"reflect"
    23  	"testing"
    24  
    25  	"storj.io/minio/pkg/bucket/policy/condition"
    26  )
    27  
    28  func TestPolicyIsAllowed(t *testing.T) {
    29  	case1Policy := Policy{
    30  		Version: DefaultVersion,
    31  		Statements: []Statement{
    32  			NewStatement(
    33  				Allow,
    34  				NewPrincipal("*"),
    35  				NewActionSet(GetBucketLocationAction, PutObjectAction),
    36  				NewResourceSet(NewResource("*", "")),
    37  				condition.NewFunctions(),
    38  			)},
    39  	}
    40  
    41  	case2Policy := Policy{
    42  		Version: DefaultVersion,
    43  		Statements: []Statement{
    44  			NewStatement(
    45  				Allow,
    46  				NewPrincipal("*"),
    47  				NewActionSet(GetObjectAction, PutObjectAction),
    48  				NewResourceSet(NewResource("mybucket", "/myobject*")),
    49  				condition.NewFunctions(),
    50  			)},
    51  	}
    52  
    53  	_, IPNet, err := net.ParseCIDR("192.168.1.0/24")
    54  	if err != nil {
    55  		t.Fatalf("unexpected error. %v\n", err)
    56  	}
    57  	func1, err := condition.NewIPAddressFunc(
    58  		condition.AWSSourceIP,
    59  		IPNet,
    60  	)
    61  	if err != nil {
    62  		t.Fatalf("unexpected error. %v\n", err)
    63  	}
    64  
    65  	case3Policy := Policy{
    66  		Version: DefaultVersion,
    67  		Statements: []Statement{
    68  			NewStatement(
    69  				Allow,
    70  				NewPrincipal("*"),
    71  				NewActionSet(GetObjectAction, PutObjectAction),
    72  				NewResourceSet(NewResource("mybucket", "/myobject*")),
    73  				condition.NewFunctions(func1),
    74  			)},
    75  	}
    76  
    77  	case4Policy := Policy{
    78  		Version: DefaultVersion,
    79  		Statements: []Statement{
    80  			NewStatement(
    81  				Deny,
    82  				NewPrincipal("*"),
    83  				NewActionSet(GetObjectAction, PutObjectAction),
    84  				NewResourceSet(NewResource("mybucket", "/myobject*")),
    85  				condition.NewFunctions(func1),
    86  			)},
    87  	}
    88  
    89  	anonGetBucketLocationArgs := Args{
    90  		AccountName:     "Q3AM3UQ867SPQQA43P2F",
    91  		Action:          GetBucketLocationAction,
    92  		BucketName:      "mybucket",
    93  		ConditionValues: map[string][]string{},
    94  	}
    95  
    96  	anonPutObjectActionArgs := Args{
    97  		AccountName: "Q3AM3UQ867SPQQA43P2F",
    98  		Action:      PutObjectAction,
    99  		BucketName:  "mybucket",
   100  		ConditionValues: map[string][]string{
   101  			"x-amz-copy-source": {"mybucket/myobject"},
   102  			"SourceIp":          {"192.168.1.10"},
   103  		},
   104  		ObjectName: "myobject",
   105  	}
   106  
   107  	anonGetObjectActionArgs := Args{
   108  		AccountName:     "Q3AM3UQ867SPQQA43P2F",
   109  		Action:          GetObjectAction,
   110  		BucketName:      "mybucket",
   111  		ConditionValues: map[string][]string{},
   112  		ObjectName:      "myobject",
   113  	}
   114  
   115  	getBucketLocationArgs := Args{
   116  		AccountName:     "Q3AM3UQ867SPQQA43P2F",
   117  		Action:          GetBucketLocationAction,
   118  		BucketName:      "mybucket",
   119  		ConditionValues: map[string][]string{},
   120  		IsOwner:         true,
   121  	}
   122  
   123  	putObjectActionArgs := Args{
   124  		AccountName: "Q3AM3UQ867SPQQA43P2F",
   125  		Action:      PutObjectAction,
   126  		BucketName:  "mybucket",
   127  		ConditionValues: map[string][]string{
   128  			"x-amz-copy-source": {"mybucket/myobject"},
   129  			"SourceIp":          {"192.168.1.10"},
   130  		},
   131  		IsOwner:    true,
   132  		ObjectName: "myobject",
   133  	}
   134  
   135  	getObjectActionArgs := Args{
   136  		AccountName:     "Q3AM3UQ867SPQQA43P2F",
   137  		Action:          GetObjectAction,
   138  		BucketName:      "mybucket",
   139  		ConditionValues: map[string][]string{},
   140  		IsOwner:         true,
   141  		ObjectName:      "myobject",
   142  	}
   143  
   144  	testCases := []struct {
   145  		policy         Policy
   146  		args           Args
   147  		expectedResult bool
   148  	}{
   149  		{case1Policy, anonGetBucketLocationArgs, true},
   150  		{case1Policy, anonPutObjectActionArgs, true},
   151  		{case1Policy, anonGetObjectActionArgs, false},
   152  		{case1Policy, getBucketLocationArgs, true},
   153  		{case1Policy, putObjectActionArgs, true},
   154  		{case1Policy, getObjectActionArgs, true},
   155  
   156  		{case2Policy, anonGetBucketLocationArgs, false},
   157  		{case2Policy, anonPutObjectActionArgs, true},
   158  		{case2Policy, anonGetObjectActionArgs, true},
   159  		{case2Policy, getBucketLocationArgs, true},
   160  		{case2Policy, putObjectActionArgs, true},
   161  		{case2Policy, getObjectActionArgs, true},
   162  
   163  		{case3Policy, anonGetBucketLocationArgs, false},
   164  		{case3Policy, anonPutObjectActionArgs, true},
   165  		{case3Policy, anonGetObjectActionArgs, false},
   166  		{case3Policy, getBucketLocationArgs, true},
   167  		{case3Policy, putObjectActionArgs, true},
   168  		{case3Policy, getObjectActionArgs, true},
   169  
   170  		{case4Policy, anonGetBucketLocationArgs, false},
   171  		{case4Policy, anonPutObjectActionArgs, false},
   172  		{case4Policy, anonGetObjectActionArgs, false},
   173  		{case4Policy, getBucketLocationArgs, true},
   174  		{case4Policy, putObjectActionArgs, false},
   175  		{case4Policy, getObjectActionArgs, true},
   176  	}
   177  
   178  	for i, testCase := range testCases {
   179  		result := testCase.policy.IsAllowed(testCase.args)
   180  
   181  		if result != testCase.expectedResult {
   182  			t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
   183  		}
   184  	}
   185  }
   186  
   187  func TestPolicyIsEmpty(t *testing.T) {
   188  	case1Policy := Policy{
   189  		Version: DefaultVersion,
   190  		Statements: []Statement{
   191  			NewStatement(
   192  				Allow,
   193  				NewPrincipal("*"),
   194  				NewActionSet(PutObjectAction),
   195  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   196  				condition.NewFunctions(),
   197  			),
   198  		},
   199  	}
   200  
   201  	case2Policy := Policy{
   202  		ID:      "MyPolicyForMyBucket",
   203  		Version: DefaultVersion,
   204  	}
   205  
   206  	testCases := []struct {
   207  		policy         Policy
   208  		expectedResult bool
   209  	}{
   210  		{case1Policy, false},
   211  		{case2Policy, true},
   212  	}
   213  
   214  	for i, testCase := range testCases {
   215  		result := testCase.policy.IsEmpty()
   216  
   217  		if result != testCase.expectedResult {
   218  			t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
   219  		}
   220  	}
   221  }
   222  
   223  func TestPolicyIsValid(t *testing.T) {
   224  	case1Policy := Policy{
   225  		Version: DefaultVersion,
   226  		Statements: []Statement{
   227  			NewStatement(
   228  				Allow,
   229  				NewPrincipal("*"),
   230  				NewActionSet(PutObjectAction),
   231  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   232  				condition.NewFunctions(),
   233  			),
   234  		},
   235  	}
   236  
   237  	case2Policy := Policy{
   238  		Version: DefaultVersion,
   239  		Statements: []Statement{
   240  			NewStatement(
   241  				Allow,
   242  				NewPrincipal("*"),
   243  				NewActionSet(PutObjectAction),
   244  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   245  				condition.NewFunctions(),
   246  			),
   247  			NewStatement(
   248  				Deny,
   249  				NewPrincipal("*"),
   250  				NewActionSet(GetObjectAction),
   251  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   252  				condition.NewFunctions(),
   253  			),
   254  		},
   255  	}
   256  
   257  	case3Policy := Policy{
   258  		Version: DefaultVersion,
   259  		Statements: []Statement{
   260  			NewStatement(
   261  				Allow,
   262  				NewPrincipal("*"),
   263  				NewActionSet(PutObjectAction),
   264  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   265  				condition.NewFunctions(),
   266  			),
   267  			NewStatement(
   268  				Deny,
   269  				NewPrincipal("*"),
   270  				NewActionSet(PutObjectAction),
   271  				NewResourceSet(NewResource("mybucket", "/yourobject*")),
   272  				condition.NewFunctions(),
   273  			),
   274  		},
   275  	}
   276  
   277  	func1, err := condition.NewNullFunc(
   278  		condition.S3XAmzCopySource,
   279  		true,
   280  	)
   281  	if err != nil {
   282  		t.Fatalf("unexpected error. %v\n", err)
   283  	}
   284  	func2, err := condition.NewNullFunc(
   285  		condition.S3XAmzServerSideEncryption,
   286  		false,
   287  	)
   288  	if err != nil {
   289  		t.Fatalf("unexpected error. %v\n", err)
   290  	}
   291  
   292  	case4Policy := Policy{
   293  		Version: DefaultVersion,
   294  		Statements: []Statement{
   295  			NewStatement(
   296  				Allow,
   297  				NewPrincipal("*"),
   298  				NewActionSet(PutObjectAction),
   299  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   300  				condition.NewFunctions(func1),
   301  			),
   302  			NewStatement(
   303  				Deny,
   304  				NewPrincipal("*"),
   305  				NewActionSet(PutObjectAction),
   306  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   307  				condition.NewFunctions(func2),
   308  			),
   309  		},
   310  	}
   311  
   312  	case5Policy := Policy{
   313  		Version: "17-10-2012",
   314  		Statements: []Statement{
   315  			NewStatement(
   316  				Allow,
   317  				NewPrincipal("*"),
   318  				NewActionSet(PutObjectAction),
   319  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   320  				condition.NewFunctions(),
   321  			),
   322  		},
   323  	}
   324  
   325  	case6Policy := Policy{
   326  		ID:      "MyPolicyForMyBucket1",
   327  		Version: DefaultVersion,
   328  		Statements: []Statement{
   329  			NewStatement(
   330  				Allow,
   331  				NewPrincipal("*"),
   332  				NewActionSet(GetObjectAction, PutObjectAction),
   333  				NewResourceSet(NewResource("mybucket", "myobject*")),
   334  				condition.NewFunctions(func1, func2),
   335  			),
   336  		},
   337  	}
   338  
   339  	case7Policy := Policy{
   340  		Version: DefaultVersion,
   341  		Statements: []Statement{
   342  			NewStatement(
   343  				Allow,
   344  				NewPrincipal("*"),
   345  				NewActionSet(PutObjectAction),
   346  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   347  				condition.NewFunctions(),
   348  			),
   349  			NewStatement(
   350  				Deny,
   351  				NewPrincipal("*"),
   352  				NewActionSet(PutObjectAction),
   353  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   354  				condition.NewFunctions(),
   355  			),
   356  		},
   357  	}
   358  
   359  	case8Policy := Policy{
   360  		Version: DefaultVersion,
   361  		Statements: []Statement{
   362  			NewStatement(
   363  				Allow,
   364  				NewPrincipal("*"),
   365  				NewActionSet(PutObjectAction),
   366  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   367  				condition.NewFunctions(),
   368  			),
   369  			NewStatement(
   370  				Allow,
   371  				NewPrincipal("*"),
   372  				NewActionSet(PutObjectAction),
   373  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   374  				condition.NewFunctions(),
   375  			),
   376  		},
   377  	}
   378  
   379  	testCases := []struct {
   380  		policy    Policy
   381  		expectErr bool
   382  	}{
   383  		{case1Policy, false},
   384  		// allowed duplicate principal.
   385  		{case2Policy, false},
   386  		// allowed duplicate principal and action.
   387  		{case3Policy, false},
   388  		// allowed duplicate principal, action and resource.
   389  		{case4Policy, false},
   390  		// Invalid version error.
   391  		{case5Policy, true},
   392  		// Invalid statement error.
   393  		{case6Policy, true},
   394  		// Duplicate statement success different effects.
   395  		{case7Policy, false},
   396  		// Duplicate statement success, duplicate statement dropped.
   397  		{case8Policy, false},
   398  	}
   399  
   400  	for i, testCase := range testCases {
   401  		err := testCase.policy.isValid()
   402  		expectErr := (err != nil)
   403  
   404  		if expectErr != testCase.expectErr {
   405  			t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
   406  		}
   407  	}
   408  }
   409  
   410  func TestPolicyMarshalJSON(t *testing.T) {
   411  	case1Policy := Policy{
   412  		ID:      "MyPolicyForMyBucket1",
   413  		Version: DefaultVersion,
   414  		Statements: []Statement{
   415  			NewStatement(
   416  				Allow,
   417  				NewPrincipal("*"),
   418  				NewActionSet(PutObjectAction),
   419  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   420  				condition.NewFunctions(),
   421  			),
   422  		},
   423  	}
   424  	case1Policy.Statements[0].SID = "SomeId1"
   425  	case1Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Sid":"SomeId1","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]}]}`)
   426  
   427  	_, IPNet1, err := net.ParseCIDR("192.168.1.0/24")
   428  	if err != nil {
   429  		t.Fatalf("unexpected error. %v\n", err)
   430  	}
   431  	func1, err := condition.NewIPAddressFunc(
   432  		condition.AWSSourceIP,
   433  		IPNet1,
   434  	)
   435  	if err != nil {
   436  		t.Fatalf("unexpected error. %v\n", err)
   437  	}
   438  
   439  	case2Policy := Policy{
   440  		Version: DefaultVersion,
   441  		Statements: []Statement{
   442  			NewStatement(
   443  				Allow,
   444  				NewPrincipal("*"),
   445  				NewActionSet(PutObjectAction),
   446  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   447  				condition.NewFunctions(),
   448  			),
   449  			NewStatement(
   450  				Deny,
   451  				NewPrincipal("*"),
   452  				NewActionSet(GetObjectAction),
   453  				NewResourceSet(NewResource("mybucket", "/yourobject*")),
   454  				condition.NewFunctions(func1),
   455  			),
   456  		},
   457  	}
   458  	case2Data := []byte(`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]},{"Effect":"Deny","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::mybucket/yourobject*"],"Condition":{"IpAddress":{"aws:SourceIp":["192.168.1.0/24"]}}}]}`)
   459  
   460  	case3Policy := Policy{
   461  		ID:      "MyPolicyForMyBucket1",
   462  		Version: DefaultVersion,
   463  		Statements: []Statement{
   464  			NewStatement(
   465  				Allow,
   466  				NewPrincipal("Q3AM3UQ867SPQQA43P2F"),
   467  				NewActionSet(PutObjectAction),
   468  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   469  				condition.NewFunctions(),
   470  			),
   471  			NewStatement(
   472  				Allow,
   473  				NewPrincipal("*"),
   474  				NewActionSet(PutObjectAction),
   475  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   476  				condition.NewFunctions(),
   477  			),
   478  		},
   479  	}
   480  	case3Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["Q3AM3UQ867SPQQA43P2F"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]}]}`)
   481  
   482  	case4Policy := Policy{
   483  		ID:      "MyPolicyForMyBucket1",
   484  		Version: DefaultVersion,
   485  		Statements: []Statement{
   486  			NewStatement(
   487  				Allow,
   488  				NewPrincipal("*"),
   489  				NewActionSet(PutObjectAction),
   490  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   491  				condition.NewFunctions(),
   492  			),
   493  			NewStatement(
   494  				Allow,
   495  				NewPrincipal("*"),
   496  				NewActionSet(GetObjectAction),
   497  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   498  				condition.NewFunctions(),
   499  			),
   500  		},
   501  	}
   502  	case4Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]}]}`)
   503  
   504  	case5Policy := Policy{
   505  		ID:      "MyPolicyForMyBucket1",
   506  		Version: DefaultVersion,
   507  		Statements: []Statement{
   508  			NewStatement(
   509  				Allow,
   510  				NewPrincipal("*"),
   511  				NewActionSet(PutObjectAction),
   512  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   513  				condition.NewFunctions(),
   514  			),
   515  			NewStatement(
   516  				Allow,
   517  				NewPrincipal("*"),
   518  				NewActionSet(PutObjectAction),
   519  				NewResourceSet(NewResource("mybucket", "/yourobject*")),
   520  				condition.NewFunctions(),
   521  			),
   522  		},
   523  	}
   524  	case5Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/yourobject*"]}]}`)
   525  
   526  	_, IPNet2, err := net.ParseCIDR("192.168.2.0/24")
   527  	if err != nil {
   528  		t.Fatalf("unexpected error. %v\n", err)
   529  	}
   530  	func2, err := condition.NewIPAddressFunc(
   531  		condition.AWSSourceIP,
   532  		IPNet2,
   533  	)
   534  	if err != nil {
   535  		t.Fatalf("unexpected error. %v\n", err)
   536  	}
   537  
   538  	case6Policy := Policy{
   539  		ID:      "MyPolicyForMyBucket1",
   540  		Version: DefaultVersion,
   541  		Statements: []Statement{
   542  			NewStatement(
   543  				Allow,
   544  				NewPrincipal("*"),
   545  				NewActionSet(PutObjectAction),
   546  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   547  				condition.NewFunctions(func1),
   548  			),
   549  			NewStatement(
   550  				Allow,
   551  				NewPrincipal("*"),
   552  				NewActionSet(PutObjectAction),
   553  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   554  				condition.NewFunctions(func2),
   555  			),
   556  		},
   557  	}
   558  	case6Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"],"Condition":{"IpAddress":{"aws:SourceIp":["192.168.1.0/24"]}}},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"],"Condition":{"IpAddress":{"aws:SourceIp":["192.168.2.0/24"]}}}]}`)
   559  
   560  	case7Policy := Policy{
   561  		ID:      "MyPolicyForMyBucket1",
   562  		Version: DefaultVersion,
   563  		Statements: []Statement{
   564  			NewStatement(
   565  				Allow,
   566  				NewPrincipal("*"),
   567  				NewActionSet(GetBucketLocationAction),
   568  				NewResourceSet(NewResource("mybucket", "")),
   569  				condition.NewFunctions(),
   570  			),
   571  		},
   572  	}
   573  	case7Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation"],"Resource":["arn:aws:s3:::mybucket"]}]}`)
   574  
   575  	case8Policy := Policy{
   576  		ID:      "MyPolicyForMyBucket1",
   577  		Version: DefaultVersion,
   578  		Statements: []Statement{
   579  			NewStatement(
   580  				Allow,
   581  				NewPrincipal("*"),
   582  				NewActionSet(GetBucketLocationAction),
   583  				NewResourceSet(NewResource("*", "")),
   584  				condition.NewFunctions(),
   585  			),
   586  		},
   587  	}
   588  	case8Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation"],"Resource":["arn:aws:s3:::*"]}]}`)
   589  
   590  	func3, err := condition.NewNullFunc(
   591  		condition.S3XAmzCopySource,
   592  		true,
   593  	)
   594  	if err != nil {
   595  		t.Fatalf("unexpected error. %v\n", err)
   596  	}
   597  	case9Policy := Policy{
   598  		ID:      "MyPolicyForMyBucket1",
   599  		Version: DefaultVersion,
   600  		Statements: []Statement{
   601  			NewStatement(
   602  				Allow,
   603  				NewPrincipal("*"),
   604  				NewActionSet(GetObjectAction, PutObjectAction),
   605  				NewResourceSet(NewResource("mybucket", "myobject*")),
   606  				condition.NewFunctions(func1, func2, func3),
   607  			),
   608  		},
   609  	}
   610  
   611  	testCases := []struct {
   612  		policy         Policy
   613  		expectedResult []byte
   614  		expectErr      bool
   615  	}{
   616  		{case1Policy, case1Data, false},
   617  		{case2Policy, case2Data, false},
   618  		{case3Policy, case3Data, false},
   619  		{case4Policy, case4Data, false},
   620  		{case5Policy, case5Data, false},
   621  		{case6Policy, case6Data, false},
   622  		{case7Policy, case7Data, false},
   623  		{case8Policy, case8Data, false},
   624  		{case9Policy, nil, true},
   625  	}
   626  
   627  	for i, testCase := range testCases {
   628  		result, err := json.Marshal(testCase.policy)
   629  		expectErr := (err != nil)
   630  
   631  		if expectErr != testCase.expectErr {
   632  			t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
   633  		}
   634  
   635  		if !testCase.expectErr {
   636  			if !reflect.DeepEqual(result, testCase.expectedResult) {
   637  				t.Fatalf("case %v: result: expected: %v, got: %v", i+1, string(testCase.expectedResult), string(result))
   638  			}
   639  		}
   640  	}
   641  }
   642  
   643  func TestPolicyUnmarshalJSON(t *testing.T) {
   644  	case1Data := []byte(`{
   645      "ID": "MyPolicyForMyBucket1",
   646      "Version": "2012-10-17",
   647      "Statement": [
   648          {
   649              "Sid": "SomeId1",
   650              "Effect": "Allow",
   651              "Principal": "*",
   652              "Action": "s3:PutObject",
   653              "Resource": "arn:aws:s3:::mybucket/myobject*"
   654          }
   655      ]
   656  }`)
   657  	case1Policy := Policy{
   658  		ID:      "MyPolicyForMyBucket1",
   659  		Version: DefaultVersion,
   660  		Statements: []Statement{
   661  			NewStatement(
   662  				Allow,
   663  				NewPrincipal("*"),
   664  				NewActionSet(PutObjectAction),
   665  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   666  				condition.NewFunctions(),
   667  			),
   668  		},
   669  	}
   670  	case1Policy.Statements[0].SID = "SomeId1"
   671  
   672  	case2Data := []byte(`{
   673      "Version": "2012-10-17",
   674      "Statement": [
   675          {
   676              "Effect": "Allow",
   677              "Principal": "*",
   678              "Action": "s3:PutObject",
   679              "Resource": "arn:aws:s3:::mybucket/myobject*"
   680          },
   681          {
   682              "Effect": "Deny",
   683              "Principal": "*",
   684              "Action": "s3:GetObject",
   685              "Resource": "arn:aws:s3:::mybucket/yourobject*",
   686              "Condition": {
   687                  "IpAddress": {
   688                      "aws:SourceIp": "192.168.1.0/24"
   689                  }
   690              }
   691          }
   692      ]
   693  }`)
   694  	_, IPNet1, err := net.ParseCIDR("192.168.1.0/24")
   695  	if err != nil {
   696  		t.Fatalf("unexpected error. %v\n", err)
   697  	}
   698  	func1, err := condition.NewIPAddressFunc(
   699  		condition.AWSSourceIP,
   700  		IPNet1,
   701  	)
   702  	if err != nil {
   703  		t.Fatalf("unexpected error. %v\n", err)
   704  	}
   705  
   706  	case2Policy := Policy{
   707  		Version: DefaultVersion,
   708  		Statements: []Statement{
   709  			NewStatement(
   710  				Allow,
   711  				NewPrincipal("*"),
   712  				NewActionSet(PutObjectAction),
   713  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   714  				condition.NewFunctions(),
   715  			),
   716  			NewStatement(
   717  				Deny,
   718  				NewPrincipal("*"),
   719  				NewActionSet(GetObjectAction),
   720  				NewResourceSet(NewResource("mybucket", "/yourobject*")),
   721  				condition.NewFunctions(func1),
   722  			),
   723  		},
   724  	}
   725  
   726  	case3Data := []byte(`{
   727      "ID": "MyPolicyForMyBucket1",
   728      "Version": "2012-10-17",
   729      "Statement": [
   730          {
   731              "Effect": "Allow",
   732              "Principal": {
   733                  "AWS": [
   734                      "Q3AM3UQ867SPQQA43P2F"
   735                  ]
   736              },
   737              "Action": "s3:PutObject",
   738              "Resource": "arn:aws:s3:::mybucket/myobject*"
   739          },
   740          {
   741              "Effect": "Allow",
   742              "Principal": "*",
   743              "Action": "s3:PutObject",
   744              "Resource": "arn:aws:s3:::mybucket/myobject*"
   745          }
   746      ]
   747  }`)
   748  	case3Policy := Policy{
   749  		ID:      "MyPolicyForMyBucket1",
   750  		Version: DefaultVersion,
   751  		Statements: []Statement{
   752  			NewStatement(
   753  				Allow,
   754  				NewPrincipal("Q3AM3UQ867SPQQA43P2F"),
   755  				NewActionSet(PutObjectAction),
   756  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   757  				condition.NewFunctions(),
   758  			),
   759  			NewStatement(
   760  				Allow,
   761  				NewPrincipal("*"),
   762  				NewActionSet(PutObjectAction),
   763  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   764  				condition.NewFunctions(),
   765  			),
   766  		},
   767  	}
   768  
   769  	case4Data := []byte(`{
   770      "ID": "MyPolicyForMyBucket1",
   771      "Version": "2012-10-17",
   772      "Statement": [
   773          {
   774              "Effect": "Allow",
   775              "Principal": "*",
   776              "Action": "s3:PutObject",
   777              "Resource": "arn:aws:s3:::mybucket/myobject*"
   778          },
   779          {
   780              "Effect": "Allow",
   781              "Principal": "*",
   782              "Action": "s3:GetObject",
   783              "Resource": "arn:aws:s3:::mybucket/myobject*"
   784          }
   785      ]
   786  }`)
   787  	case4Policy := Policy{
   788  		ID:      "MyPolicyForMyBucket1",
   789  		Version: DefaultVersion,
   790  		Statements: []Statement{
   791  			NewStatement(
   792  				Allow,
   793  				NewPrincipal("*"),
   794  				NewActionSet(PutObjectAction),
   795  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   796  				condition.NewFunctions(),
   797  			),
   798  			NewStatement(
   799  				Allow,
   800  				NewPrincipal("*"),
   801  				NewActionSet(GetObjectAction),
   802  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   803  				condition.NewFunctions(),
   804  			),
   805  		},
   806  	}
   807  
   808  	case5Data := []byte(`{
   809      "ID": "MyPolicyForMyBucket1",
   810      "Version": "2012-10-17",
   811      "Statement": [
   812          {
   813              "Effect": "Allow",
   814              "Principal": "*",
   815              "Action": "s3:PutObject",
   816              "Resource": "arn:aws:s3:::mybucket/myobject*"
   817          },
   818          {
   819              "Effect": "Allow",
   820              "Principal": "*",
   821              "Action": "s3:PutObject",
   822              "Resource": "arn:aws:s3:::mybucket/yourobject*"
   823          }
   824      ]
   825  }`)
   826  	case5Policy := Policy{
   827  		ID:      "MyPolicyForMyBucket1",
   828  		Version: DefaultVersion,
   829  		Statements: []Statement{
   830  			NewStatement(
   831  				Allow,
   832  				NewPrincipal("*"),
   833  				NewActionSet(PutObjectAction),
   834  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   835  				condition.NewFunctions(),
   836  			),
   837  			NewStatement(
   838  				Allow,
   839  				NewPrincipal("*"),
   840  				NewActionSet(PutObjectAction),
   841  				NewResourceSet(NewResource("mybucket", "/yourobject*")),
   842  				condition.NewFunctions(),
   843  			),
   844  		},
   845  	}
   846  
   847  	case6Data := []byte(`{
   848      "ID": "MyPolicyForMyBucket1",
   849      "Version": "2012-10-17",
   850      "Statement": [
   851          {
   852              "Effect": "Allow",
   853              "Principal": "*",
   854              "Action": "s3:PutObject",
   855              "Resource": "arn:aws:s3:::mybucket/myobject*",
   856              "Condition": {
   857                  "IpAddress": {
   858                      "aws:SourceIp": "192.168.1.0/24"
   859                  }
   860              }
   861          },
   862          {
   863              "Effect": "Allow",
   864              "Principal": "*",
   865              "Action": "s3:PutObject",
   866              "Resource": "arn:aws:s3:::mybucket/myobject*",
   867              "Condition": {
   868                  "IpAddress": {
   869                      "aws:SourceIp": "192.168.2.0/24"
   870                  }
   871              }
   872          }
   873      ]
   874  }`)
   875  	_, IPNet2, err := net.ParseCIDR("192.168.2.0/24")
   876  	if err != nil {
   877  		t.Fatalf("unexpected error. %v\n", err)
   878  	}
   879  	func2, err := condition.NewIPAddressFunc(
   880  		condition.AWSSourceIP,
   881  		IPNet2,
   882  	)
   883  	if err != nil {
   884  		t.Fatalf("unexpected error. %v\n", err)
   885  	}
   886  
   887  	case6Policy := Policy{
   888  		ID:      "MyPolicyForMyBucket1",
   889  		Version: DefaultVersion,
   890  		Statements: []Statement{
   891  			NewStatement(
   892  				Allow,
   893  				NewPrincipal("*"),
   894  				NewActionSet(PutObjectAction),
   895  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   896  				condition.NewFunctions(func1),
   897  			),
   898  			NewStatement(
   899  				Allow,
   900  				NewPrincipal("*"),
   901  				NewActionSet(PutObjectAction),
   902  				NewResourceSet(NewResource("mybucket", "/myobject*")),
   903  				condition.NewFunctions(func2),
   904  			),
   905  		},
   906  	}
   907  
   908  	case7Data := []byte(`{
   909      "ID": "MyPolicyForMyBucket1",
   910      "Version": "2012-10-17",
   911      "Statement": [
   912          {
   913              "Effect": "Allow",
   914              "Principal": "*",
   915              "Action": "s3:GetBucketLocation",
   916              "Resource": "arn:aws:s3:::mybucket"
   917          }
   918      ]
   919  }`)
   920  
   921  	case7Policy := Policy{
   922  		ID:      "MyPolicyForMyBucket1",
   923  		Version: DefaultVersion,
   924  		Statements: []Statement{
   925  			NewStatement(
   926  				Allow,
   927  				NewPrincipal("*"),
   928  				NewActionSet(GetBucketLocationAction),
   929  				NewResourceSet(NewResource("mybucket", "")),
   930  				condition.NewFunctions(),
   931  			),
   932  		},
   933  	}
   934  
   935  	case8Data := []byte(`{
   936      "ID": "MyPolicyForMyBucket1",
   937      "Version": "2012-10-17",
   938      "Statement": [
   939          {
   940              "Effect": "Allow",
   941              "Principal": "*",
   942              "Action": "s3:GetBucketLocation",
   943              "Resource": "arn:aws:s3:::*"
   944          }
   945      ]
   946  }`)
   947  
   948  	case8Policy := Policy{
   949  		ID:      "MyPolicyForMyBucket1",
   950  		Version: DefaultVersion,
   951  		Statements: []Statement{
   952  			NewStatement(
   953  				Allow,
   954  				NewPrincipal("*"),
   955  				NewActionSet(GetBucketLocationAction),
   956  				NewResourceSet(NewResource("*", "")),
   957  				condition.NewFunctions(),
   958  			),
   959  		},
   960  	}
   961  
   962  	case9Data := []byte(`{
   963      "ID": "MyPolicyForMyBucket1",
   964      "Version": "17-10-2012",
   965      "Statement": [
   966          {
   967              "Effect": "Allow",
   968              "Principal": "*",
   969              "Action": "s3:PutObject",
   970              "Resource": "arn:aws:s3:::mybucket/myobject*"
   971          }
   972      ]
   973  }`)
   974  
   975  	case10Data := []byte(`{
   976      "ID": "MyPolicyForMyBucket1",
   977      "Version": "2012-10-17",
   978      "Statement": [
   979          {
   980              "Effect": "Allow",
   981              "Principal": "*",
   982              "Action": "s3:PutObject",
   983              "Resource": "arn:aws:s3:::mybucket/myobject*"
   984          },
   985          {
   986              "Effect": "Allow",
   987              "Principal": "*",
   988              "Action": "s3:PutObject",
   989              "Resource": "arn:aws:s3:::mybucket/myobject*"
   990          }
   991      ]
   992  }`)
   993  
   994  	case10Policy := Policy{
   995  		ID:      "MyPolicyForMyBucket1",
   996  		Version: DefaultVersion,
   997  		Statements: []Statement{
   998  			NewStatement(
   999  				Allow,
  1000  				NewPrincipal("*"),
  1001  				NewActionSet(PutObjectAction),
  1002  				NewResourceSet(NewResource("mybucket", "myobject*")),
  1003  				condition.NewFunctions(),
  1004  			),
  1005  		},
  1006  	}
  1007  
  1008  	case11Data := []byte(`{
  1009      "ID": "MyPolicyForMyBucket1",
  1010      "Version": "2012-10-17",
  1011      "Statement": [
  1012          {
  1013              "Effect": "Allow",
  1014              "Principal": "*",
  1015              "Action": "s3:PutObject",
  1016              "Resource": "arn:aws:s3:::mybucket/myobject*"
  1017          },
  1018          {
  1019              "Effect": "Deny",
  1020              "Principal": "*",
  1021              "Action": "s3:PutObject",
  1022              "Resource": "arn:aws:s3:::mybucket/myobject*"
  1023          }
  1024      ]
  1025  }`)
  1026  
  1027  	case11Policy := Policy{
  1028  		ID:      "MyPolicyForMyBucket1",
  1029  		Version: DefaultVersion,
  1030  		Statements: []Statement{
  1031  			NewStatement(
  1032  				Allow,
  1033  				NewPrincipal("*"),
  1034  				NewActionSet(PutObjectAction),
  1035  				NewResourceSet(NewResource("mybucket", "myobject*")),
  1036  				condition.NewFunctions(),
  1037  			),
  1038  			NewStatement(
  1039  				Deny,
  1040  				NewPrincipal("*"),
  1041  				NewActionSet(PutObjectAction),
  1042  				NewResourceSet(NewResource("mybucket", "myobject*")),
  1043  				condition.NewFunctions(),
  1044  			),
  1045  		},
  1046  	}
  1047  
  1048  	testCases := []struct {
  1049  		data           []byte
  1050  		expectedResult Policy
  1051  		expectErr      bool
  1052  	}{
  1053  		{case1Data, case1Policy, false},
  1054  		{case2Data, case2Policy, false},
  1055  		{case3Data, case3Policy, false},
  1056  		{case4Data, case4Policy, false},
  1057  		{case5Data, case5Policy, false},
  1058  		{case6Data, case6Policy, false},
  1059  		{case7Data, case7Policy, false},
  1060  		{case8Data, case8Policy, false},
  1061  		// Invalid version error.
  1062  		{case9Data, Policy{}, true},
  1063  		// Duplicate statement success, duplicate statement removed.
  1064  		{case10Data, case10Policy, false},
  1065  		// Duplicate statement success (Effect differs).
  1066  		{case11Data, case11Policy, false},
  1067  	}
  1068  
  1069  	for i, testCase := range testCases {
  1070  		var result Policy
  1071  		err := json.Unmarshal(testCase.data, &result)
  1072  		expectErr := (err != nil)
  1073  
  1074  		if expectErr != testCase.expectErr {
  1075  			t.Errorf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
  1076  		}
  1077  
  1078  		if !testCase.expectErr {
  1079  			if !reflect.DeepEqual(result, testCase.expectedResult) {
  1080  				t.Errorf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result)
  1081  			}
  1082  		}
  1083  	}
  1084  }
  1085  
  1086  func TestPolicyValidate(t *testing.T) {
  1087  	case1Policy := Policy{
  1088  		Version: DefaultVersion,
  1089  		Statements: []Statement{
  1090  			NewStatement(
  1091  				Allow,
  1092  				NewPrincipal("*"),
  1093  				NewActionSet(PutObjectAction),
  1094  				NewResourceSet(NewResource("mybucket", "/myobject*")),
  1095  				condition.NewFunctions(),
  1096  			),
  1097  		},
  1098  	}
  1099  
  1100  	func1, err := condition.NewNullFunc(
  1101  		condition.S3XAmzCopySource,
  1102  		true,
  1103  	)
  1104  	if err != nil {
  1105  		t.Fatalf("unexpected error. %v\n", err)
  1106  	}
  1107  	func2, err := condition.NewNullFunc(
  1108  		condition.S3XAmzServerSideEncryption,
  1109  		false,
  1110  	)
  1111  	if err != nil {
  1112  		t.Fatalf("unexpected error. %v\n", err)
  1113  	}
  1114  	case2Policy := Policy{
  1115  		ID:      "MyPolicyForMyBucket1",
  1116  		Version: DefaultVersion,
  1117  		Statements: []Statement{
  1118  			NewStatement(
  1119  				Allow,
  1120  				NewPrincipal("*"),
  1121  				NewActionSet(GetObjectAction, PutObjectAction),
  1122  				NewResourceSet(NewResource("mybucket", "myobject*")),
  1123  				condition.NewFunctions(func1, func2),
  1124  			),
  1125  		},
  1126  	}
  1127  
  1128  	testCases := []struct {
  1129  		policy     Policy
  1130  		bucketName string
  1131  		expectErr  bool
  1132  	}{
  1133  		{case1Policy, "mybucket", false},
  1134  		{case2Policy, "yourbucket", true},
  1135  		{case1Policy, "yourbucket", true},
  1136  	}
  1137  
  1138  	for i, testCase := range testCases {
  1139  		err := testCase.policy.Validate(testCase.bucketName)
  1140  		expectErr := (err != nil)
  1141  
  1142  		if expectErr != testCase.expectErr {
  1143  			t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
  1144  		}
  1145  	}
  1146  }
  1147  
  1148  func TestPolicyMerge(t *testing.T) {
  1149  	testCases := []struct {
  1150  		policy string
  1151  	}{
  1152  		{`{
  1153      "Version": "2012-10-17",
  1154      "Id": "S3PolicyId1",
  1155      "Statement": [
  1156          {
  1157              "Sid": "statement1",
  1158              "Effect": "Deny",
  1159              "Principal": "*",
  1160  	    "Action":["s3:GetObject", "s3:PutObject"],
  1161  	    "Resource": "arn:aws:s3:::awsexamplebucket1/*"
  1162          }
  1163      ]
  1164  }`},
  1165  		{`{
  1166      "Version": "2012-10-17",
  1167      "Id": "S3PolicyId1",
  1168      "Statement": [
  1169          {
  1170              "Sid": "statement1",
  1171              "Effect": "Allow",
  1172              "Principal": "*",
  1173              "Action":"s3:GetObject",
  1174              "Resource": "arn:aws:s3:::awsexamplebucket1/*",
  1175              "Condition" : {
  1176                  "IpAddress" : {
  1177                      "aws:SourceIp": "192.0.2.0/24"
  1178                  },
  1179                  "NotIpAddress" : {
  1180                      "aws:SourceIp": "192.0.2.188/32"
  1181                  }
  1182              }
  1183          }
  1184      ]
  1185  }`},
  1186  		{`{
  1187      "Version": "2012-10-17",
  1188      "Statement": [
  1189         {
  1190              "Sid": "cross-account permission to user in your own account",
  1191              "Effect": "Allow",
  1192              "Principal": {
  1193                  "AWS": "arn:aws:iam::123456789012:user/Dave"
  1194              },
  1195              "Action": "s3:PutObject",
  1196              "Resource": "arn:aws:s3:::awsexamplebucket1/*"
  1197          },
  1198          {
  1199              "Sid": "Deny your user permission to upload object if copy source is not /bucket/folder",
  1200              "Effect": "Deny",
  1201              "Principal": {
  1202                  "AWS": "arn:aws:iam::123456789012:user/Dave"
  1203              },
  1204              "Action": "s3:PutObject",
  1205              "Resource": "arn:aws:s3:::awsexamplebucket1/*",
  1206              "Condition": {
  1207                  "StringNotLike": {
  1208                      "s3:x-amz-copy-source": "awsexamplebucket1/public/*"
  1209                  }
  1210              }
  1211          }
  1212      ]
  1213  }`},
  1214  	}
  1215  
  1216  	for i, testCase := range testCases {
  1217  		var p Policy
  1218  		err := json.Unmarshal([]byte(testCase.policy), &p)
  1219  		if err != nil {
  1220  			t.Fatalf("case %v: unexpected error: %v", i+1, err)
  1221  		}
  1222  
  1223  		var clonedPolicy Policy
  1224  		clonedPolicy = clonedPolicy.Merge(p)
  1225  
  1226  		j, err := json.Marshal(clonedPolicy)
  1227  		if err != nil {
  1228  			t.Fatalf("case %v: unexpected error: %v", i+1, err)
  1229  		}
  1230  
  1231  		err = json.Unmarshal(j, &clonedPolicy)
  1232  		if err != nil {
  1233  			t.Fatalf("case %v: unexpected error: %v", i+1, err)
  1234  		}
  1235  
  1236  		if !clonedPolicy.Statements[0].Equals(p.Statements[0]) {
  1237  			t.Fatalf("case %v: different policy outcome", i+1)
  1238  		}
  1239  	}
  1240  }