github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/cloud/cloud.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // Package cloud defines an API end point for functions dealing with 5 // the controller's cloud definition, and cloud credentials. 6 package cloud 7 8 import ( 9 "github.com/juju/errors" 10 "gopkg.in/juju/names.v2" 11 12 "github.com/juju/juju/apiserver/common" 13 "github.com/juju/juju/apiserver/facade" 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/cloud" 16 "github.com/juju/juju/environs" 17 "github.com/juju/juju/permission" 18 "github.com/juju/juju/state" 19 ) 20 21 func init() { 22 common.RegisterStandardFacade("Cloud", 1, newFacade) 23 } 24 25 // CloudAPI implements the model manager interface and is 26 // the concrete implementation of the api end point. 27 type CloudAPI struct { 28 backend Backend 29 authorizer facade.Authorizer 30 apiUser names.UserTag 31 getCredentialsAuthFunc common.GetAuthFunc 32 } 33 34 func newFacade(st *state.State, _ facade.Resources, auth facade.Authorizer) (*CloudAPI, error) { 35 return NewCloudAPI(NewStateBackend(st), auth) 36 } 37 38 // NewCloudAPI creates a new API server endpoint for managing the controller's 39 // cloud definition and cloud credentials. 40 func NewCloudAPI(backend Backend, authorizer facade.Authorizer) (*CloudAPI, error) { 41 42 if !authorizer.AuthClient() { 43 return nil, common.ErrPerm 44 } 45 46 getUserAuthFunc := func() (common.AuthFunc, error) { 47 authUser, _ := authorizer.GetAuthTag().(names.UserTag) 48 isAdmin, err := authorizer.HasPermission(permission.SuperuserAccess, backend.ControllerTag()) 49 if err != nil && !errors.IsNotFound(err) { 50 return nil, err 51 } 52 return func(tag names.Tag) bool { 53 userTag, ok := tag.(names.UserTag) 54 if !ok { 55 return false 56 } 57 return isAdmin || userTag.Canonical() == authUser.Canonical() 58 }, nil 59 } 60 return &CloudAPI{ 61 backend: backend, 62 authorizer: authorizer, 63 getCredentialsAuthFunc: getUserAuthFunc, 64 }, nil 65 } 66 67 // Clouds returns the definitions of all clouds supported by the controller. 68 func (api *CloudAPI) Clouds() (params.CloudsResult, error) { 69 var result params.CloudsResult 70 clouds, err := api.backend.Clouds() 71 if err != nil { 72 return result, err 73 } 74 result.Clouds = make(map[string]params.Cloud) 75 for tag, cloud := range clouds { 76 paramsCloud := cloudToParams(cloud) 77 result.Clouds[tag.String()] = paramsCloud 78 } 79 return result, nil 80 } 81 82 // Cloud returns the cloud definitions for the specified clouds. 83 func (api *CloudAPI) Cloud(args params.Entities) (params.CloudResults, error) { 84 results := params.CloudResults{ 85 Results: make([]params.CloudResult, len(args.Entities)), 86 } 87 one := func(arg params.Entity) (*params.Cloud, error) { 88 tag, err := names.ParseCloudTag(arg.Tag) 89 if err != nil { 90 return nil, err 91 } 92 cloud, err := api.backend.Cloud(tag.Id()) 93 if err != nil { 94 return nil, err 95 } 96 paramsCloud := cloudToParams(cloud) 97 return ¶msCloud, nil 98 } 99 for i, arg := range args.Entities { 100 cloud, err := one(arg) 101 if err != nil { 102 results.Results[i].Error = common.ServerError(err) 103 } else { 104 results.Results[i].Cloud = cloud 105 } 106 } 107 return results, nil 108 } 109 110 func cloudToParams(cloud cloud.Cloud) params.Cloud { 111 authTypes := make([]string, len(cloud.AuthTypes)) 112 for i, authType := range cloud.AuthTypes { 113 authTypes[i] = string(authType) 114 } 115 regions := make([]params.CloudRegion, len(cloud.Regions)) 116 for i, region := range cloud.Regions { 117 regions[i] = params.CloudRegion{ 118 Name: region.Name, 119 Endpoint: region.Endpoint, 120 IdentityEndpoint: region.IdentityEndpoint, 121 StorageEndpoint: region.StorageEndpoint, 122 } 123 } 124 return params.Cloud{ 125 Type: cloud.Type, 126 AuthTypes: authTypes, 127 Endpoint: cloud.Endpoint, 128 IdentityEndpoint: cloud.IdentityEndpoint, 129 StorageEndpoint: cloud.StorageEndpoint, 130 Regions: regions, 131 } 132 } 133 134 // DefaultCloud returns the tag of the cloud that models will be 135 // created in by default. 136 func (api *CloudAPI) DefaultCloud() (params.StringResult, error) { 137 controllerModel, err := api.backend.ControllerModel() 138 if err != nil { 139 return params.StringResult{}, err 140 } 141 142 return params.StringResult{ 143 Result: names.NewCloudTag(controllerModel.Cloud()).String(), 144 }, nil 145 } 146 147 // UserCredentials returns the cloud credentials for a set of users. 148 func (api *CloudAPI) UserCredentials(args params.UserClouds) (params.StringsResults, error) { 149 results := params.StringsResults{ 150 Results: make([]params.StringsResult, len(args.UserClouds)), 151 } 152 authFunc, err := api.getCredentialsAuthFunc() 153 if err != nil { 154 return results, err 155 } 156 for i, arg := range args.UserClouds { 157 userTag, err := names.ParseUserTag(arg.UserTag) 158 if err != nil { 159 results.Results[i].Error = common.ServerError(err) 160 continue 161 } 162 if !authFunc(userTag) { 163 results.Results[i].Error = common.ServerError(common.ErrPerm) 164 continue 165 } 166 cloudTag, err := names.ParseCloudTag(arg.CloudTag) 167 if err != nil { 168 results.Results[i].Error = common.ServerError(err) 169 continue 170 } 171 cloudCredentials, err := api.backend.CloudCredentials(userTag, cloudTag.Id()) 172 if err != nil { 173 results.Results[i].Error = common.ServerError(err) 174 continue 175 } 176 out := make([]string, 0, len(cloudCredentials)) 177 for tagId := range cloudCredentials { 178 out = append(out, names.NewCloudCredentialTag(tagId).String()) 179 } 180 results.Results[i].Result = out 181 } 182 return results, nil 183 } 184 185 // UpdateCredentials updates a set of cloud credentials. 186 func (api *CloudAPI) UpdateCredentials(args params.UpdateCloudCredentials) (params.ErrorResults, error) { 187 results := params.ErrorResults{ 188 Results: make([]params.ErrorResult, len(args.Credentials)), 189 } 190 authFunc, err := api.getCredentialsAuthFunc() 191 if err != nil { 192 return results, err 193 } 194 for i, arg := range args.Credentials { 195 tag, err := names.ParseCloudCredentialTag(arg.Tag) 196 if err != nil { 197 results.Results[i].Error = common.ServerError(err) 198 continue 199 } 200 // NOTE(axw) if we add ACLs for cloud credentials, we'll need 201 // to change this auth check. 202 if !authFunc(tag.Owner()) { 203 results.Results[i].Error = common.ServerError(common.ErrPerm) 204 continue 205 } 206 in := cloud.NewCredential( 207 cloud.AuthType(arg.Credential.AuthType), 208 arg.Credential.Attributes, 209 ) 210 if err := api.backend.UpdateCloudCredential(tag, in); err != nil { 211 results.Results[i].Error = common.ServerError(err) 212 continue 213 } 214 } 215 return results, nil 216 } 217 218 // RevokeCredentials revokes a set of cloud credentials. 219 func (api *CloudAPI) RevokeCredentials(args params.Entities) (params.ErrorResults, error) { 220 results := params.ErrorResults{ 221 Results: make([]params.ErrorResult, len(args.Entities)), 222 } 223 authFunc, err := api.getCredentialsAuthFunc() 224 if err != nil { 225 return results, err 226 } 227 for i, arg := range args.Entities { 228 tag, err := names.ParseCloudCredentialTag(arg.Tag) 229 if err != nil { 230 results.Results[i].Error = common.ServerError(err) 231 continue 232 } 233 // NOTE(axw) if we add ACLs for cloud credentials, we'll need 234 // to change this auth check. 235 if !authFunc(tag.Owner()) { 236 results.Results[i].Error = common.ServerError(common.ErrPerm) 237 continue 238 } 239 if err := api.backend.RemoveCloudCredential(tag); err != nil { 240 results.Results[i].Error = common.ServerError(err) 241 } 242 } 243 return results, nil 244 } 245 246 // Credential returns the specified cloud credential for each tag, minus secrets. 247 func (api *CloudAPI) Credential(args params.Entities) (params.CloudCredentialResults, error) { 248 results := params.CloudCredentialResults{ 249 Results: make([]params.CloudCredentialResult, len(args.Entities)), 250 } 251 authFunc, err := api.getCredentialsAuthFunc() 252 if err != nil { 253 return results, err 254 } 255 256 for i, arg := range args.Entities { 257 credentialTag, err := names.ParseCloudCredentialTag(arg.Tag) 258 if err != nil { 259 results.Results[i].Error = common.ServerError(err) 260 continue 261 } 262 if !authFunc(credentialTag.Owner()) { 263 results.Results[i].Error = common.ServerError(common.ErrPerm) 264 continue 265 } 266 267 // Helper to look up and cache credential schemas for clouds. 268 schemaCache := make(map[string]map[cloud.AuthType]cloud.CredentialSchema) 269 credentialSchemas := func() (map[cloud.AuthType]cloud.CredentialSchema, error) { 270 cloudName := credentialTag.Cloud().Id() 271 if s, ok := schemaCache[cloudName]; ok { 272 return s, nil 273 } 274 cloud, err := api.backend.Cloud(cloudName) 275 if err != nil { 276 return nil, err 277 } 278 provider, err := environs.Provider(cloud.Type) 279 if err != nil { 280 return nil, err 281 } 282 schema := provider.CredentialSchemas() 283 schemaCache[cloudName] = schema 284 return schema, nil 285 } 286 cloudCredentials, err := api.backend.CloudCredentials(credentialTag.Owner(), credentialTag.Cloud().Id()) 287 if err != nil { 288 results.Results[i].Error = common.ServerError(err) 289 continue 290 } 291 292 cred, ok := cloudCredentials[credentialTag.Canonical()] 293 if !ok { 294 results.Results[i].Error = common.ServerError(errors.NotFoundf("credential %q", credentialTag.Name())) 295 continue 296 } 297 298 schemas, err := credentialSchemas() 299 if err != nil { 300 results.Results[i].Error = common.ServerError(err) 301 continue 302 } 303 304 attrs := cred.Attributes() 305 var redacted []string 306 // Mask out the secrets. 307 if s, ok := schemas[cred.AuthType()]; ok { 308 for _, attr := range s { 309 if attr.Hidden { 310 delete(attrs, attr.Name) 311 redacted = append(redacted, attr.Name) 312 } 313 } 314 } 315 results.Results[i].Result = ¶ms.CloudCredential{ 316 AuthType: string(cred.AuthType()), 317 Attributes: attrs, 318 Redacted: redacted, 319 } 320 } 321 return results, nil 322 }