github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/modelmanager/modelmanager.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // Package modelmanager defines an API end point for functions dealing with 5 // models. Creating, listing and sharing models. This facade is available at 6 // the root of the controller API, and as such, there is no implicit Model 7 // assocated. 8 package modelmanager 9 10 import ( 11 "time" 12 13 "github.com/juju/errors" 14 "github.com/juju/loggo" 15 "github.com/juju/names" 16 "github.com/juju/txn" 17 "github.com/juju/version" 18 19 "github.com/juju/juju/apiserver/common" 20 "github.com/juju/juju/apiserver/params" 21 "github.com/juju/juju/controller/modelmanager" 22 "github.com/juju/juju/environs/config" 23 "github.com/juju/juju/juju/permission" 24 "github.com/juju/juju/state" 25 "github.com/juju/juju/tools" 26 ) 27 28 var logger = loggo.GetLogger("juju.apiserver.modelmanager") 29 30 func init() { 31 common.RegisterStandardFacade("ModelManager", 2, newFacade) 32 } 33 34 // ModelManager defines the methods on the modelmanager API endpoint. 35 type ModelManager interface { 36 ConfigSkeleton(args params.ModelSkeletonConfigArgs) (params.ModelConfigResult, error) 37 CreateModel(args params.ModelCreateArgs) (params.Model, error) 38 ListModels(user params.Entity) (params.UserModelList, error) 39 } 40 41 // ModelManagerAPI implements the model manager interface and is 42 // the concrete implementation of the api end point. 43 type ModelManagerAPI struct { 44 state Backend 45 authorizer common.Authorizer 46 toolsFinder *common.ToolsFinder 47 apiUser names.UserTag 48 isAdmin bool 49 } 50 51 var _ ModelManager = (*ModelManagerAPI)(nil) 52 53 func newFacade(st *state.State, resources *common.Resources, auth common.Authorizer) (*ModelManagerAPI, error) { 54 return NewModelManagerAPI(NewStateBackend(st), auth) 55 } 56 57 // NewModelManagerAPI creates a new api server endpoint for managing 58 // models. 59 func NewModelManagerAPI(st Backend, authorizer common.Authorizer) (*ModelManagerAPI, error) { 60 if !authorizer.AuthClient() { 61 return nil, common.ErrPerm 62 } 63 // Since we know this is a user tag (because AuthClient is true), 64 // we just do the type assertion to the UserTag. 65 apiUser, _ := authorizer.GetAuthTag().(names.UserTag) 66 // Pretty much all of the user manager methods have special casing for admin 67 // users, so look once when we start and remember if the user is an admin. 68 isAdmin, err := st.IsControllerAdministrator(apiUser) 69 if err != nil { 70 return nil, errors.Trace(err) 71 } 72 urlGetter := common.NewToolsURLGetter(st.ModelUUID(), st) 73 return &ModelManagerAPI{ 74 state: st, 75 authorizer: authorizer, 76 toolsFinder: common.NewToolsFinder(st, st, urlGetter), 77 apiUser: apiUser, 78 isAdmin: isAdmin, 79 }, nil 80 } 81 82 // authCheck checks if the user is acting on their own behalf, or if they 83 // are an administrator acting on behalf of another user. 84 func (m *ModelManagerAPI) authCheck(user names.UserTag) error { 85 if m.isAdmin { 86 logger.Tracef("%q is a controller admin", m.apiUser.Canonical()) 87 return nil 88 } 89 90 // We can't just compare the UserTags themselves as the provider part 91 // may be unset, and gets replaced with 'local'. We must compare against 92 // the Canonical value of the user tag. 93 if m.apiUser.Canonical() == user.Canonical() { 94 return nil 95 } 96 return common.ErrPerm 97 } 98 99 // ConfigSource describes a type that is able to provide config. 100 // Abstracted primarily for testing. 101 type ConfigSource interface { 102 Config() (*config.Config, error) 103 } 104 105 // ConfigSkeleton returns config values to be used as a starting point for the 106 // API caller to construct a valid model specific config. The provider 107 // and region params are there for future use, and current behaviour expects 108 // both of these to be empty. 109 func (mm *ModelManagerAPI) ConfigSkeleton(args params.ModelSkeletonConfigArgs) (params.ModelConfigResult, error) { 110 var result params.ModelConfigResult 111 if args.Region != "" { 112 return result, errors.NotValidf("region value %q", args.Region) 113 } 114 115 controllerEnv, err := mm.state.ControllerModel() 116 if err != nil { 117 return result, errors.Trace(err) 118 } 119 config, err := mm.configSkeleton(controllerEnv, args.Provider) 120 if err != nil { 121 return result, errors.Trace(err) 122 } 123 124 result.Config = config 125 return result, nil 126 } 127 128 func (mm *ModelManagerAPI) configSkeleton(source ConfigSource, requestedProviderType string) (map[string]interface{}, error) { 129 baseConfig, err := source.Config() 130 if err != nil { 131 return nil, errors.Trace(err) 132 } 133 if requestedProviderType != "" && baseConfig.Type() != requestedProviderType { 134 return nil, errors.Errorf( 135 "cannot create new model with credentials for provider type %q on controller with provider type %q", 136 requestedProviderType, baseConfig.Type()) 137 } 138 baseMap := baseConfig.AllAttrs() 139 140 fields, err := modelmanager.RestrictedProviderFields(baseConfig.Type()) 141 if err != nil { 142 return nil, errors.Trace(err) 143 } 144 145 var result = make(map[string]interface{}) 146 for _, field := range fields { 147 if value, found := baseMap[field]; found { 148 result[field] = value 149 } 150 } 151 return result, nil 152 } 153 154 func (mm *ModelManagerAPI) newModelConfig(args params.ModelCreateArgs, source ConfigSource) (*config.Config, error) { 155 // For now, we just smash to the two maps together as we store 156 // the account values and the model config together in the 157 // *config.Config instance. 158 joint := make(map[string]interface{}) 159 for key, value := range args.Config { 160 joint[key] = value 161 } 162 // Account info overrides any config values. 163 for key, value := range args.Account { 164 joint[key] = value 165 } 166 if _, ok := joint["uuid"]; ok { 167 return nil, errors.New("uuid is generated, you cannot specify one") 168 } 169 baseConfig, err := source.Config() 170 if err != nil { 171 return nil, errors.Trace(err) 172 } 173 creator := modelmanager.ModelConfigCreator{ 174 FindTools: func(n version.Number) (tools.List, error) { 175 result, err := mm.toolsFinder.FindTools(params.FindToolsParams{ 176 Number: n, 177 }) 178 if err != nil { 179 return nil, errors.Trace(err) 180 } 181 return result.List, nil 182 }, 183 } 184 return creator.NewModelConfig(mm.isAdmin, baseConfig, joint) 185 } 186 187 // CreateModel creates a new model using the account and 188 // model config specified in the args. 189 func (mm *ModelManagerAPI) CreateModel(args params.ModelCreateArgs) (params.Model, error) { 190 result := params.Model{} 191 // Get the controller model first. We need it both for the state 192 // server owner and the ability to get the config. 193 controllerModel, err := mm.state.ControllerModel() 194 if err != nil { 195 return result, errors.Trace(err) 196 } 197 198 ownerTag, err := names.ParseUserTag(args.OwnerTag) 199 if err != nil { 200 return result, errors.Trace(err) 201 } 202 203 // Any user is able to create themselves an model (until real fine 204 // grain permissions are available), and admins (the creator of the state 205 // server model) are able to create models for other people. 206 err = mm.authCheck(ownerTag) 207 if err != nil { 208 return result, errors.Trace(err) 209 } 210 211 newConfig, err := mm.newModelConfig(args, controllerModel) 212 if err != nil { 213 return result, errors.Annotate(err, "failed to create config") 214 } 215 // NOTE: check the agent-version of the config, and if it is > the current 216 // version, it is not supported, also check existing tools, and if we don't 217 // have tools for that version, also die. 218 model, st, err := mm.state.NewModel(state.ModelArgs{Config: newConfig, Owner: ownerTag}) 219 if err != nil { 220 return result, errors.Annotate(err, "failed to create new model") 221 } 222 defer st.Close() 223 224 result.Name = model.Name() 225 result.UUID = model.UUID() 226 result.OwnerTag = model.Owner().String() 227 228 return result, nil 229 } 230 231 // ListModels returns the models that the specified user 232 // has access to in the current server. Only that controller owner 233 // can list models for any user (at this stage). Other users 234 // can only ask about their own models. 235 func (mm *ModelManagerAPI) ListModels(user params.Entity) (params.UserModelList, error) { 236 result := params.UserModelList{} 237 238 userTag, err := names.ParseUserTag(user.Tag) 239 if err != nil { 240 return result, errors.Trace(err) 241 } 242 243 err = mm.authCheck(userTag) 244 if err != nil { 245 return result, errors.Trace(err) 246 } 247 248 models, err := mm.state.ModelsForUser(userTag) 249 if err != nil { 250 return result, errors.Trace(err) 251 } 252 253 for _, model := range models { 254 var lastConn *time.Time 255 userLastConn, err := model.LastConnection() 256 if err != nil { 257 if !state.IsNeverConnectedError(err) { 258 return result, errors.Trace(err) 259 } 260 } else { 261 lastConn = &userLastConn 262 } 263 result.UserModels = append(result.UserModels, params.UserModel{ 264 Model: params.Model{ 265 Name: model.Name(), 266 UUID: model.UUID(), 267 OwnerTag: model.Owner().String(), 268 }, 269 LastConnection: lastConn, 270 }) 271 } 272 273 return result, nil 274 } 275 276 // ModelInfo returns information about the specified models. 277 func (m *ModelManagerAPI) ModelInfo(args params.Entities) (params.ModelInfoResults, error) { 278 results := params.ModelInfoResults{ 279 Results: make([]params.ModelInfoResult, len(args.Entities)), 280 } 281 282 getModelInfo := func(arg params.Entity) (params.ModelInfo, error) { 283 tag, err := names.ParseModelTag(arg.Tag) 284 if err != nil { 285 return params.ModelInfo{}, err 286 } 287 288 st, err := m.state.ForModel(tag) 289 if errors.IsNotFound(err) { 290 return params.ModelInfo{}, common.ErrPerm 291 } else if err != nil { 292 return params.ModelInfo{}, err 293 } 294 defer st.Close() 295 296 model, err := st.Model() 297 if errors.IsNotFound(err) { 298 return params.ModelInfo{}, common.ErrPerm 299 } else if err != nil { 300 return params.ModelInfo{}, err 301 } 302 303 cfg, err := model.Config() 304 if err != nil { 305 return params.ModelInfo{}, err 306 } 307 users, err := model.Users() 308 if err != nil { 309 return params.ModelInfo{}, err 310 } 311 status, err := model.Status() 312 if err != nil { 313 return params.ModelInfo{}, err 314 } 315 316 owner := model.Owner() 317 info := params.ModelInfo{ 318 Name: cfg.Name(), 319 UUID: cfg.UUID(), 320 ControllerUUID: cfg.ControllerUUID(), 321 OwnerTag: owner.String(), 322 Life: params.Life(model.Life().String()), 323 Status: common.EntityStatusFromState(status), 324 ProviderType: cfg.Type(), 325 DefaultSeries: config.PreferredSeries(cfg), 326 } 327 328 authorizedOwner := m.authCheck(owner) == nil 329 for _, user := range users { 330 if !authorizedOwner && m.authCheck(user.UserTag()) != nil { 331 // The authenticated user is neither the owner 332 // nor administrator, nor the model user, so 333 // has no business knowing about the model user. 334 continue 335 } 336 userInfo, err := common.ModelUserInfo(user) 337 if err != nil { 338 return params.ModelInfo{}, errors.Trace(err) 339 } 340 info.Users = append(info.Users, userInfo) 341 } 342 343 if len(info.Users) == 0 { 344 // No users, which means the authenticated user doesn't 345 // have access to the model. 346 return params.ModelInfo{}, common.ErrPerm 347 } 348 349 return info, nil 350 } 351 352 for i, arg := range args.Entities { 353 modelInfo, err := getModelInfo(arg) 354 if err != nil { 355 results.Results[i].Error = common.ServerError(err) 356 continue 357 } 358 results.Results[i].Result = &modelInfo 359 } 360 return results, nil 361 } 362 363 // ModifyModelAccess changes the model access granted to users. 364 func (m *ModelManagerAPI) ModifyModelAccess(args params.ModifyModelAccessRequest) (result params.ErrorResults, err error) { 365 result = params.ErrorResults{ 366 Results: make([]params.ErrorResult, len(args.Changes)), 367 } 368 if len(args.Changes) == 0 { 369 return result, nil 370 } 371 372 for i, arg := range args.Changes { 373 modelAccess, err := FromModelAccessParam(arg.Access) 374 if err != nil { 375 err = errors.Annotate(err, "could not modify model access") 376 result.Results[i].Error = common.ServerError(err) 377 continue 378 } 379 380 targetUserTag, err := names.ParseUserTag(arg.UserTag) 381 if err != nil { 382 result.Results[i].Error = common.ServerError(errors.Annotate(err, "could not modify model access")) 383 continue 384 } 385 modelTag, err := names.ParseModelTag(arg.ModelTag) 386 if err != nil { 387 result.Results[i].Error = common.ServerError(errors.Annotate(err, "could not modify model access")) 388 continue 389 } 390 391 result.Results[i].Error = common.ServerError( 392 ChangeModelAccess(m.state, modelTag, m.apiUser, targetUserTag, arg.Action, modelAccess, m.isAdmin)) 393 } 394 return result, nil 395 } 396 397 // resolveStateAccess returns the state representation of the logical model 398 // access type. 399 func resolveStateAccess(access permission.ModelAccess) (state.ModelAccess, error) { 400 var fail state.ModelAccess 401 switch access { 402 case permission.ModelReadAccess: 403 return state.ModelReadAccess, nil 404 case permission.ModelWriteAccess: 405 // TODO: Initially, we'll map "write" access to admin-level access. 406 // Post Juju-2.0, support for more nuanced access will be added to the 407 // permission business logic and state model. 408 return state.ModelAdminAccess, nil 409 } 410 logger.Errorf("invalid access permission: %+v", access) 411 return fail, errors.Errorf("invalid access permission") 412 } 413 414 // isGreaterAccess returns whether the new access provides more permissions 415 // than the current access. 416 // TODO(cmars): If/when more access types are implemented in state, 417 // the implementation of this function will certainly need to change, and it 418 // should be abstracted away to juju/permission as pure business logic 419 // instead of operating on state values. 420 func isGreaterAccess(currentAccess, newAccess state.ModelAccess) bool { 421 if currentAccess == state.ModelReadAccess && newAccess == state.ModelAdminAccess { 422 return true 423 } 424 return false 425 } 426 427 func userAuthorizedToChangeAccess(st Backend, userIsAdmin bool, userTag names.UserTag) error { 428 if userIsAdmin { 429 // Just confirm that the model that has been given is a valid model. 430 _, err := st.Model() 431 if err != nil { 432 return errors.Trace(err) 433 } 434 return nil 435 } 436 437 // Get the current user's ModelUser for the Model to see if the user has 438 // permission to grant or revoke permissions on the model. 439 currentUser, err := st.ModelUser(userTag) 440 if err != nil { 441 if errors.IsNotFound(err) { 442 // No, this user doesn't have permission. 443 return common.ErrPerm 444 } 445 return errors.Annotate(err, "could not retrieve user") 446 } 447 if currentUser.Access() != state.ModelAdminAccess { 448 return common.ErrPerm 449 } 450 return nil 451 } 452 453 // ChangeModelAccess performs the requested access grant or revoke action for the 454 // specified user on the specified model. 455 func ChangeModelAccess(accessor Backend, modelTag names.ModelTag, apiUser, targetUserTag names.UserTag, action params.ModelAction, access permission.ModelAccess, userIsAdmin bool) error { 456 st, err := accessor.ForModel(modelTag) 457 if err != nil { 458 return errors.Annotate(err, "could not lookup model") 459 } 460 defer st.Close() 461 462 if err := userAuthorizedToChangeAccess(st, userIsAdmin, apiUser); err != nil { 463 return errors.Trace(err) 464 } 465 466 stateAccess, err := resolveStateAccess(access) 467 if err != nil { 468 return errors.Annotate(err, "could not resolve model access") 469 } 470 471 switch action { 472 case params.GrantModelAccess: 473 _, err = st.AddModelUser(state.ModelUserSpec{User: targetUserTag, CreatedBy: apiUser, Access: stateAccess}) 474 if errors.IsAlreadyExists(err) { 475 modelUser, err := st.ModelUser(targetUserTag) 476 if errors.IsNotFound(err) { 477 // Conflicts with prior check, must be inconsistent state. 478 err = txn.ErrExcessiveContention 479 } 480 if err != nil { 481 return errors.Annotate(err, "could not look up model access for user") 482 } 483 484 // Only set access if greater access is being granted. 485 if isGreaterAccess(modelUser.Access(), stateAccess) { 486 err = modelUser.SetAccess(stateAccess) 487 if err != nil { 488 return errors.Annotate(err, "could not set model access for user") 489 } 490 } else { 491 return errors.Errorf("user already has %q access", modelUser.Access()) 492 } 493 return nil 494 } 495 return errors.Annotate(err, "could not grant model access") 496 497 case params.RevokeModelAccess: 498 if stateAccess == state.ModelReadAccess { 499 // Revoking read access removes all access. 500 err := st.RemoveModelUser(targetUserTag) 501 return errors.Annotate(err, "could not revoke model access") 502 503 } else if stateAccess == state.ModelAdminAccess { 504 // Revoking admin access sets read-only. 505 modelUser, err := st.ModelUser(targetUserTag) 506 if err != nil { 507 return errors.Annotate(err, "could not look up model access for user") 508 } 509 err = modelUser.SetAccess(state.ModelReadAccess) 510 return errors.Annotate(err, "could not set model access to read-only") 511 512 } else { 513 return errors.Errorf("don't know how to revoke %q access", stateAccess) 514 } 515 516 default: 517 return errors.Errorf("unknown action %q", action) 518 } 519 } 520 521 // FromModelAccessParam returns the logical model access type from the API wireformat type. 522 func FromModelAccessParam(paramAccess params.ModelAccessPermission) (permission.ModelAccess, error) { 523 var fail permission.ModelAccess 524 switch paramAccess { 525 case params.ModelReadAccess: 526 return permission.ModelReadAccess, nil 527 case params.ModelWriteAccess: 528 return permission.ModelWriteAccess, nil 529 } 530 return fail, errors.Errorf("invalid model access permission %q", paramAccess) 531 }