storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/iam/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 iampolicy 18 19 import ( 20 "encoding/json" 21 "io" 22 "strings" 23 24 "github.com/minio/minio-go/v7/pkg/set" 25 26 "storj.io/minio/pkg/bucket/policy" 27 ) 28 29 // DefaultVersion - default policy version as per AWS S3 specification. 30 const DefaultVersion = "2012-10-17" 31 32 // Args - arguments to policy to check whether it is allowed 33 type Args struct { 34 AccountName string `json:"account"` 35 Groups []string `json:"groups"` 36 Action Action `json:"action"` 37 BucketName string `json:"bucket"` 38 ConditionValues map[string][]string `json:"conditions"` 39 IsOwner bool `json:"owner"` 40 ObjectName string `json:"object"` 41 Claims map[string]interface{} `json:"claims"` 42 DenyOnly bool `json:"denyOnly"` // only applies deny 43 } 44 45 // GetPoliciesFromClaims returns the list of policies to be applied for this 46 // incoming request, extracting the information from input JWT claims. 47 func GetPoliciesFromClaims(claims map[string]interface{}, policyClaimName string) (set.StringSet, bool) { 48 s := set.NewStringSet() 49 pname, ok := claims[policyClaimName] 50 if !ok { 51 return s, false 52 } 53 pnames, ok := pname.([]interface{}) 54 if !ok { 55 pnameStr, ok := pname.(string) 56 if ok { 57 for _, pname := range strings.Split(pnameStr, ",") { 58 pname = strings.TrimSpace(pname) 59 if pname == "" { 60 // ignore any empty strings, considerate 61 // towards some user errors. 62 continue 63 } 64 s.Add(pname) 65 } 66 return s, true 67 } 68 return s, false 69 } 70 for _, pname := range pnames { 71 pnameStr, ok := pname.(string) 72 if ok { 73 for _, pnameStr := range strings.Split(pnameStr, ",") { 74 pnameStr = strings.TrimSpace(pnameStr) 75 if pnameStr == "" { 76 // ignore any empty strings, considerate 77 // towards some user errors. 78 continue 79 } 80 s.Add(pnameStr) 81 } 82 } 83 } 84 return s, true 85 } 86 87 // GetPolicies returns the list of policies to be applied for this 88 // incoming request, extracting the information from JWT claims. 89 func (a Args) GetPolicies(policyClaimName string) (set.StringSet, bool) { 90 return GetPoliciesFromClaims(a.Claims, policyClaimName) 91 } 92 93 // Policy - iam bucket iamp. 94 type Policy struct { 95 ID policy.ID `json:"ID,omitempty"` 96 Version string 97 Statements []Statement `json:"Statement"` 98 } 99 100 // IsAllowed - checks given policy args is allowed to continue the Rest API. 101 func (iamp Policy) IsAllowed(args Args) bool { 102 // Check all deny statements. If any one statement denies, return false. 103 for _, statement := range iamp.Statements { 104 if statement.Effect == policy.Deny { 105 if !statement.IsAllowed(args) { 106 return false 107 } 108 } 109 } 110 111 // Applied any 'Deny' only policies, if we have 112 // reached here it means that there were no 'Deny' 113 // policies - this function mainly used for 114 // specific scenarios where we only want to validate 115 // 'Deny' only policies. 116 if args.DenyOnly { 117 return true 118 } 119 120 // For owner, its allowed by default. 121 if args.IsOwner { 122 return true 123 } 124 125 // Check all allow statements. If any one statement allows, return true. 126 for _, statement := range iamp.Statements { 127 if statement.Effect == policy.Allow { 128 if statement.IsAllowed(args) { 129 return true 130 } 131 } 132 } 133 134 return false 135 } 136 137 // IsEmpty - returns whether policy is empty or not. 138 func (iamp Policy) IsEmpty() bool { 139 return len(iamp.Statements) == 0 140 } 141 142 // isValid - checks if Policy is valid or not. 143 func (iamp Policy) isValid() error { 144 if iamp.Version != DefaultVersion && iamp.Version != "" { 145 return Errorf("invalid version '%v'", iamp.Version) 146 } 147 148 for _, statement := range iamp.Statements { 149 if err := statement.isValid(); err != nil { 150 return err 151 } 152 } 153 return nil 154 } 155 156 // Merge merges two policies documents and drop 157 // duplicate statements if any. 158 func (iamp Policy) Merge(input Policy) Policy { 159 var mergedPolicy Policy 160 if iamp.Version != "" { 161 mergedPolicy.Version = iamp.Version 162 } else { 163 mergedPolicy.Version = input.Version 164 } 165 for _, st := range iamp.Statements { 166 mergedPolicy.Statements = append(mergedPolicy.Statements, st.Clone()) 167 } 168 for _, st := range input.Statements { 169 mergedPolicy.Statements = append(mergedPolicy.Statements, st.Clone()) 170 } 171 mergedPolicy.dropDuplicateStatements() 172 return mergedPolicy 173 } 174 175 func (iamp *Policy) dropDuplicateStatements() { 176 redo: 177 for i := range iamp.Statements { 178 for j, statement := range iamp.Statements[i+1:] { 179 if !iamp.Statements[i].Equals(statement) { 180 continue 181 } 182 iamp.Statements = append(iamp.Statements[:j], iamp.Statements[j+1:]...) 183 goto redo 184 } 185 } 186 } 187 188 // UnmarshalJSON - decodes JSON data to Iamp. 189 func (iamp *Policy) UnmarshalJSON(data []byte) error { 190 // subtype to avoid recursive call to UnmarshalJSON() 191 type subPolicy Policy 192 var sp subPolicy 193 if err := json.Unmarshal(data, &sp); err != nil { 194 return err 195 } 196 197 p := Policy(sp) 198 p.dropDuplicateStatements() 199 *iamp = p 200 return nil 201 } 202 203 // Validate - validates all statements are for given bucket or not. 204 func (iamp Policy) Validate() error { 205 return iamp.isValid() 206 } 207 208 // ParseConfig - parses data in given reader to Iamp. 209 func ParseConfig(reader io.Reader) (*Policy, error) { 210 var iamp Policy 211 212 decoder := json.NewDecoder(reader) 213 decoder.DisallowUnknownFields() 214 if err := decoder.Decode(&iamp); err != nil { 215 return nil, Errorf("%w", err) 216 } 217 218 return &iamp, iamp.Validate() 219 }