1// Copyright (c) 2015-2021 MinIO, Inc. 2// 3// This file is part of MinIO Object Storage stack 4// 5// This program is free software: you can redistribute it and/or modify 6// it under the terms of the GNU Affero General Public License as published by 7// the Free Software Foundation, either version 3 of the License, or 8// (at your option) any later version. 9// 10// This program is distributed in the hope that it will be useful 11// but WITHOUT ANY WARRANTY; without even the implied warranty of 12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13// GNU Affero General Public License for more details. 14// 15// You should have received a copy of the GNU Affero General Public License 16// along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18package iampolicy 19 20import ( 21 "strings" 22 23 "github.com/minio/pkg/bucket/policy" 24 "github.com/minio/pkg/bucket/policy/condition" 25) 26 27// Statement - iam policy statement. 28type Statement struct { 29 SID policy.ID `json:"Sid,omitempty"` 30 Effect policy.Effect `json:"Effect"` 31 Actions ActionSet `json:"Action"` 32 Resources ResourceSet `json:"Resource,omitempty"` 33 Conditions condition.Functions `json:"Condition,omitempty"` 34} 35 36// IsAllowed - checks given policy args is allowed to continue the Rest API. 37func (statement Statement) IsAllowed(args Args) bool { 38 check := func() bool { 39 if !statement.Actions.Match(args.Action) { 40 return false 41 } 42 43 resource := args.BucketName 44 if args.ObjectName != "" { 45 if !strings.HasPrefix(args.ObjectName, "/") { 46 resource += "/" 47 } 48 49 resource += args.ObjectName 50 } else { 51 resource += "/" 52 } 53 54 // For admin statements, resource match can be ignored. 55 if !statement.Resources.Match(resource, args.ConditionValues) && !statement.isAdmin() { 56 return false 57 } 58 59 return statement.Conditions.Evaluate(args.ConditionValues) 60 } 61 62 return statement.Effect.IsAllowed(check()) 63} 64func (statement Statement) isAdmin() bool { 65 for action := range statement.Actions { 66 if AdminAction(action).IsValid() { 67 return true 68 } 69 } 70 return false 71} 72 73// isValid - checks whether statement is valid or not. 74func (statement Statement) isValid() error { 75 if !statement.Effect.IsValid() { 76 return Errorf("invalid Effect %v", statement.Effect) 77 } 78 79 if len(statement.Actions) == 0 { 80 return Errorf("Action must not be empty") 81 } 82 83 if statement.isAdmin() { 84 if err := statement.Actions.ValidateAdmin(); err != nil { 85 return err 86 } 87 for action := range statement.Actions { 88 keys := statement.Conditions.Keys() 89 keyDiff := keys.Difference(adminActionConditionKeyMap[action]) 90 if !keyDiff.IsEmpty() { 91 return Errorf("unsupported condition keys '%v' used for action '%v'", keyDiff, action) 92 } 93 } 94 return nil 95 } 96 97 if !statement.SID.IsValid() { 98 return Errorf("invalid SID %v", statement.SID) 99 } 100 101 if len(statement.Resources) == 0 { 102 return Errorf("Resource must not be empty") 103 } 104 105 if err := statement.Resources.Validate(); err != nil { 106 return err 107 } 108 109 if err := statement.Actions.Validate(); err != nil { 110 return err 111 } 112 113 for action := range statement.Actions { 114 if !statement.Resources.objectResourceExists() && !statement.Resources.bucketResourceExists() { 115 return Errorf("unsupported Resource found %v for action %v", statement.Resources, action) 116 } 117 118 keys := statement.Conditions.Keys() 119 keyDiff := keys.Difference(iamActionConditionKeyMap.Lookup(action)) 120 if !keyDiff.IsEmpty() { 121 return Errorf("unsupported condition keys '%v' used for action '%v'", keyDiff, action) 122 } 123 } 124 125 return nil 126} 127 128// Validate - validates Statement is for given bucket or not. 129func (statement Statement) Validate() error { 130 return statement.isValid() 131} 132 133// Equals checks if two statements are equal 134func (statement Statement) Equals(st Statement) bool { 135 if statement.Effect != st.Effect { 136 return false 137 } 138 if !statement.Actions.Equals(st.Actions) { 139 return false 140 } 141 if !statement.Resources.Equals(st.Resources) { 142 return false 143 } 144 if !statement.Conditions.Equals(st.Conditions) { 145 return false 146 } 147 return true 148} 149 150// Clone clones Statement structure 151func (statement Statement) Clone() Statement { 152 return NewStatement(statement.Effect, statement.Actions.Clone(), 153 statement.Resources.Clone(), statement.Conditions.Clone()) 154} 155 156// NewStatement - creates new statement. 157func NewStatement(effect policy.Effect, actionSet ActionSet, resourceSet ResourceSet, conditions condition.Functions) Statement { 158 return Statement{ 159 Effect: effect, 160 Actions: actionSet, 161 Resources: resourceSet, 162 Conditions: conditions, 163 } 164} 165