storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/bucket/policy/policy.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  	"io"
    22  )
    23  
    24  // DefaultVersion - default policy version as per AWS S3 specification.
    25  const DefaultVersion = "2012-10-17"
    26  
    27  // Args - arguments to policy to check whether it is allowed
    28  type Args struct {
    29  	AccountName     string              `json:"account"`
    30  	Groups          []string            `json:"groups"`
    31  	Action          Action              `json:"action"`
    32  	BucketName      string              `json:"bucket"`
    33  	ConditionValues map[string][]string `json:"conditions"`
    34  	IsOwner         bool                `json:"owner"`
    35  	ObjectName      string              `json:"object"`
    36  }
    37  
    38  // Policy - bucket policy.
    39  type Policy struct {
    40  	ID         ID `json:"ID,omitempty"`
    41  	Version    string
    42  	Statements []Statement `json:"Statement"`
    43  }
    44  
    45  // IsAllowed - checks given policy args is allowed to continue the Rest API.
    46  func (policy Policy) IsAllowed(args Args) bool {
    47  	// Check all deny statements. If any one statement denies, return false.
    48  	for _, statement := range policy.Statements {
    49  		if statement.Effect == Deny {
    50  			if !statement.IsAllowed(args) {
    51  				return false
    52  			}
    53  		}
    54  	}
    55  
    56  	// For owner, its allowed by default.
    57  	if args.IsOwner {
    58  		return true
    59  	}
    60  
    61  	// Check all allow statements. If any one statement allows, return true.
    62  	for _, statement := range policy.Statements {
    63  		if statement.Effect == Allow {
    64  			if statement.IsAllowed(args) {
    65  				return true
    66  			}
    67  		}
    68  	}
    69  
    70  	return false
    71  }
    72  
    73  // IsEmpty - returns whether policy is empty or not.
    74  func (policy Policy) IsEmpty() bool {
    75  	return len(policy.Statements) == 0
    76  }
    77  
    78  // isValid - checks if Policy is valid or not.
    79  func (policy Policy) isValid() error {
    80  	if policy.Version != DefaultVersion && policy.Version != "" {
    81  		return Errorf("invalid version '%v'", policy.Version)
    82  	}
    83  
    84  	for _, statement := range policy.Statements {
    85  		if err := statement.isValid(); err != nil {
    86  			return err
    87  		}
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  // MarshalJSON - encodes Policy to JSON data.
    94  func (policy Policy) MarshalJSON() ([]byte, error) {
    95  	if err := policy.isValid(); err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	// subtype to avoid recursive call to MarshalJSON()
   100  	type subPolicy Policy
   101  	return json.Marshal(subPolicy(policy))
   102  }
   103  
   104  // Merge merges two policies documents and drop
   105  // duplicate statements if any.
   106  func (policy Policy) Merge(input Policy) Policy {
   107  	var mergedPolicy Policy
   108  	if policy.Version != "" {
   109  		mergedPolicy.Version = policy.Version
   110  	} else {
   111  		mergedPolicy.Version = input.Version
   112  	}
   113  	for _, st := range policy.Statements {
   114  		mergedPolicy.Statements = append(mergedPolicy.Statements, st.Clone())
   115  	}
   116  	for _, st := range input.Statements {
   117  		mergedPolicy.Statements = append(mergedPolicy.Statements, st.Clone())
   118  	}
   119  	mergedPolicy.dropDuplicateStatements()
   120  	return mergedPolicy
   121  }
   122  
   123  func (policy *Policy) dropDuplicateStatements() {
   124  redo:
   125  	for i := range policy.Statements {
   126  		for j, statement := range policy.Statements[i+1:] {
   127  			if !policy.Statements[i].Equals(statement) {
   128  				continue
   129  			}
   130  			policy.Statements = append(policy.Statements[:j], policy.Statements[j+1:]...)
   131  			goto redo
   132  		}
   133  	}
   134  }
   135  
   136  // UnmarshalJSON - decodes JSON data to Policy.
   137  func (policy *Policy) UnmarshalJSON(data []byte) error {
   138  	// subtype to avoid recursive call to UnmarshalJSON()
   139  	type subPolicy Policy
   140  	var sp subPolicy
   141  	if err := json.Unmarshal(data, &sp); err != nil {
   142  		return err
   143  	}
   144  
   145  	p := Policy(sp)
   146  	if err := p.isValid(); err != nil {
   147  		return err
   148  	}
   149  
   150  	p.dropDuplicateStatements()
   151  
   152  	*policy = p
   153  
   154  	return nil
   155  }
   156  
   157  // Validate - validates all statements are for given bucket or not.
   158  func (policy Policy) Validate(bucketName string) error {
   159  	if err := policy.isValid(); err != nil {
   160  		return err
   161  	}
   162  
   163  	for _, statement := range policy.Statements {
   164  		if err := statement.Validate(bucketName); err != nil {
   165  			return err
   166  		}
   167  	}
   168  
   169  	return nil
   170  }
   171  
   172  // ParseConfig - parses data in given reader to Policy.
   173  func ParseConfig(reader io.Reader, bucketName string) (*Policy, error) {
   174  	var policy Policy
   175  
   176  	decoder := json.NewDecoder(reader)
   177  	decoder.DisallowUnknownFields()
   178  	if err := decoder.Decode(&policy); err != nil {
   179  		return nil, Errorf("%w", err)
   180  	}
   181  
   182  	err := policy.Validate(bucketName)
   183  	return &policy, err
   184  }