1/* 2Copyright 2017 WALLIX 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package awsservices 18 19import ( 20 "regexp" 21 "strings" 22 "sync" 23 24 awssdk "github.com/aws/aws-sdk-go/aws" 25 "github.com/aws/aws-sdk-go/service/iam" 26 "github.com/wallix/awless/cloud" 27) 28 29func GetCloudServicesForAPIs(apis ...string) (services []cloud.Service) { 30 unique := make(map[string]struct{}) 31 for _, api := range apis { 32 if name, ok := ServicePerAPI[api]; ok { 33 if service, exists := cloud.ServiceRegistry[name]; exists { 34 if _, done := unique[name]; !done { 35 unique[name] = struct{}{} 36 services = append(services, service) 37 } 38 } 39 } 40 } 41 return 42} 43 44func GetCloudServicesForTypes(types ...string) (services []cloud.Service) { 45 unique := make(map[string]struct{}) 46 for _, typ := range types { 47 if name, ok := ServicePerResourceType[typ]; ok { 48 if service, exists := cloud.ServiceRegistry[name]; exists { 49 if _, done := unique[name]; !done { 50 unique[name] = struct{}{} 51 services = append(services, service) 52 } 53 } 54 } 55 } 56 return 57} 58 59func ResourceTypesPerServiceName() map[string][]string { 60 out := make(map[string][]string) 61 for rT, s := range ServicePerResourceType { 62 out[s] = append(out[s], rT) 63 } 64 return out 65} 66 67var arnResourceInfoRegex = regexp.MustCompile(`(root)|([\w-.]*)/([\w-./]*)`) 68 69type Identity struct { 70 Account, Arn, UserId, ResourceType, ResourcePath, Resource string 71} 72 73func (i *Identity) IsRoot() bool { 74 return i.Resource == "root" 75} 76 77func (i *Identity) IsUserType() bool { 78 return i.ResourceType == "user" 79} 80 81func (s *Access) GetIdentity() (*Identity, error) { 82 resp, err := s.STSAPI.GetCallerIdentity(nil) 83 if err != nil { 84 return nil, err 85 } 86 87 ident := &Identity{ 88 Account: awssdk.StringValue(resp.Account), 89 Arn: awssdk.StringValue(resp.Arn), 90 UserId: awssdk.StringValue(resp.UserId), 91 } 92 93 splits := strings.Split(ident.Arn, ":") 94 if l := len(splits); l > 0 { 95 ident.ResourcePath = splits[l-1] 96 matches := arnResourceInfoRegex.FindStringSubmatch(ident.ResourcePath) 97 if len(matches) == 4 { 98 if matches[1] == "root" { 99 ident.Resource = "root" 100 ident.ResourceType = "user" 101 } else { 102 ident.ResourceType = matches[2] 103 ident.Resource = matches[3] 104 } 105 } 106 } 107 108 return ident, nil 109} 110 111type UserPolicies struct { 112 Username string 113 Inlined []string 114 Attached []string 115 ByGroup map[string][]string 116} 117 118func (s *Access) GetUserPolicies(username string) (*UserPolicies, error) { 119 var wg sync.WaitGroup 120 121 all := &UserPolicies{ 122 Username: username, 123 ByGroup: make(map[string][]string), 124 } 125 126 errc := make(chan error, 4) 127 128 wg.Add(1) 129 go func() { 130 defer wg.Done() 131 policies, err := s.ListUserPolicies(&iam.ListUserPoliciesInput{ 132 UserName: awssdk.String(username), 133 }) 134 if err != nil { 135 errc <- err 136 return 137 } 138 139 for _, name := range policies.PolicyNames { 140 all.Inlined = append(all.Inlined, awssdk.StringValue(name)) 141 } 142 }() 143 144 wg.Add(1) 145 go func() { 146 defer wg.Done() 147 attached, err := s.ListAttachedUserPolicies(&iam.ListAttachedUserPoliciesInput{ 148 UserName: awssdk.String(username), 149 }) 150 if err != nil { 151 errc <- err 152 return 153 } 154 155 for _, pol := range attached.AttachedPolicies { 156 all.Attached = append(all.Attached, awssdk.StringValue(pol.PolicyName)) 157 } 158 }() 159 160 wg.Add(1) 161 go func() { 162 defer wg.Done() 163 groups, err := s.ListGroupsForUser(&iam.ListGroupsForUserInput{ 164 UserName: awssdk.String(username), 165 }) 166 if err != nil { 167 errc <- err 168 return 169 } 170 171 type result struct { 172 group, policy string 173 } 174 resultC := make(chan result) 175 var wgg sync.WaitGroup 176 for _, group := range groups.Groups { 177 wgg.Add(1) 178 go func(name string) { 179 defer wgg.Done() 180 181 output, err := s.ListAttachedGroupPolicies(&iam.ListAttachedGroupPoliciesInput{ 182 GroupName: awssdk.String(name), 183 }) 184 if err != nil { 185 errc <- err 186 return 187 } 188 for _, pol := range output.AttachedPolicies { 189 resultC <- result{group: name, policy: awssdk.StringValue(pol.PolicyName)} 190 } 191 }(awssdk.StringValue(group.GroupName)) 192 } 193 194 go func() { 195 wgg.Wait() 196 close(resultC) 197 }() 198 199 for res := range resultC { 200 all.ByGroup[res.group] = append(all.ByGroup[res.group], res.policy) 201 } 202 }() 203 204 go func() { 205 wg.Wait() 206 close(errc) 207 }() 208 209 for e := range errc { 210 return all, e 211 } 212 213 return all, nil 214} 215