storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/iam/policy/statement.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 iampolicy
    18  
    19  import (
    20  	"strings"
    21  
    22  	"storj.io/minio/pkg/bucket/policy"
    23  	"storj.io/minio/pkg/bucket/policy/condition"
    24  )
    25  
    26  // Statement - iam policy statement.
    27  type Statement struct {
    28  	SID        policy.ID           `json:"Sid,omitempty"`
    29  	Effect     policy.Effect       `json:"Effect"`
    30  	Actions    ActionSet           `json:"Action"`
    31  	Resources  ResourceSet         `json:"Resource,omitempty"`
    32  	Conditions condition.Functions `json:"Condition,omitempty"`
    33  }
    34  
    35  // IsAllowed - checks given policy args is allowed to continue the Rest API.
    36  func (statement Statement) IsAllowed(args Args) bool {
    37  	check := func() bool {
    38  		if !statement.Actions.Match(args.Action) {
    39  			return false
    40  		}
    41  
    42  		resource := args.BucketName
    43  		if args.ObjectName != "" {
    44  			if !strings.HasPrefix(args.ObjectName, "/") {
    45  				resource += "/"
    46  			}
    47  
    48  			resource += args.ObjectName
    49  		} else {
    50  			resource += "/"
    51  		}
    52  
    53  		// For admin statements, resource match can be ignored.
    54  		if !statement.Resources.Match(resource, args.ConditionValues) && !statement.isAdmin() {
    55  			return false
    56  		}
    57  
    58  		return statement.Conditions.Evaluate(args.ConditionValues)
    59  	}
    60  
    61  	return statement.Effect.IsAllowed(check())
    62  }
    63  func (statement Statement) isAdmin() bool {
    64  	for action := range statement.Actions {
    65  		if AdminAction(action).IsValid() {
    66  			return true
    67  		}
    68  	}
    69  	return false
    70  }
    71  
    72  // isValid - checks whether statement is valid or not.
    73  func (statement Statement) isValid() error {
    74  	if !statement.Effect.IsValid() {
    75  		return Errorf("invalid Effect %v", statement.Effect)
    76  	}
    77  
    78  	if len(statement.Actions) == 0 {
    79  		return Errorf("Action must not be empty")
    80  	}
    81  
    82  	if statement.isAdmin() {
    83  		if err := statement.Actions.ValidateAdmin(); err != nil {
    84  			return err
    85  		}
    86  		for action := range statement.Actions {
    87  			keys := statement.Conditions.Keys()
    88  			keyDiff := keys.Difference(adminActionConditionKeyMap[action])
    89  			if !keyDiff.IsEmpty() {
    90  				return Errorf("unsupported condition keys '%v' used for action '%v'", keyDiff, action)
    91  			}
    92  		}
    93  		return nil
    94  	}
    95  
    96  	if !statement.SID.IsValid() {
    97  		return Errorf("invalid SID %v", statement.SID)
    98  	}
    99  
   100  	if len(statement.Resources) == 0 {
   101  		return Errorf("Resource must not be empty")
   102  	}
   103  
   104  	if err := statement.Resources.Validate(); err != nil {
   105  		return err
   106  	}
   107  
   108  	if err := statement.Actions.Validate(); err != nil {
   109  		return err
   110  	}
   111  
   112  	for action := range statement.Actions {
   113  		if !statement.Resources.objectResourceExists() && !statement.Resources.bucketResourceExists() {
   114  			return Errorf("unsupported Resource found %v for action %v", statement.Resources, action)
   115  		}
   116  
   117  		keys := statement.Conditions.Keys()
   118  		keyDiff := keys.Difference(iamActionConditionKeyMap.Lookup(action))
   119  		if !keyDiff.IsEmpty() {
   120  			return Errorf("unsupported condition keys '%v' used for action '%v'", keyDiff, action)
   121  		}
   122  	}
   123  
   124  	return nil
   125  }
   126  
   127  // Validate - validates Statement is for given bucket or not.
   128  func (statement Statement) Validate() error {
   129  	return statement.isValid()
   130  }
   131  
   132  // Equals checks if two statements are equal
   133  func (statement Statement) Equals(st Statement) bool {
   134  	if statement.Effect != st.Effect {
   135  		return false
   136  	}
   137  	if !statement.Actions.Equals(st.Actions) {
   138  		return false
   139  	}
   140  	if !statement.Resources.Equals(st.Resources) {
   141  		return false
   142  	}
   143  	if !statement.Conditions.Equals(st.Conditions) {
   144  		return false
   145  	}
   146  	return true
   147  }
   148  
   149  // Clone clones Statement structure
   150  func (statement Statement) Clone() Statement {
   151  	return NewStatement(statement.Effect, statement.Actions.Clone(),
   152  		statement.Resources.Clone(), statement.Conditions.Clone())
   153  }
   154  
   155  // NewStatement - creates new statement.
   156  func NewStatement(effect policy.Effect, actionSet ActionSet, resourceSet ResourceSet, conditions condition.Functions) Statement {
   157  	return Statement{
   158  		Effect:     effect,
   159  		Actions:    actionSet,
   160  		Resources:  resourceSet,
   161  		Conditions: conditions,
   162  	}
   163  }