launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/state/apiserver/keyupdater/authorisedkeys.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package keyupdater 5 6 import ( 7 errgo "launchpad.net/errgo/errors" 8 "launchpad.net/juju-core/errors" 9 "launchpad.net/juju-core/state" 10 "launchpad.net/juju-core/state/api/params" 11 "launchpad.net/juju-core/state/apiserver/common" 12 "launchpad.net/juju-core/state/watcher" 13 "launchpad.net/juju-core/utils/ssh" 14 ) 15 16 var mask = errgo.Mask 17 18 // KeyUpdater defines the methods on the keyupdater API end point. 19 type KeyUpdater interface { 20 AuthorisedKeys(args params.Entities) (params.StringsResults, error) 21 WatchAuthorisedKeys(args params.Entities) (params.NotifyWatchResults, error) 22 } 23 24 // KeyUpdaterAPI implements the KeyUpdater interface and is the concrete 25 // implementation of the api end point. 26 type KeyUpdaterAPI struct { 27 state *state.State 28 resources *common.Resources 29 authorizer common.Authorizer 30 getCanRead common.GetAuthFunc 31 } 32 33 var _ KeyUpdater = (*KeyUpdaterAPI)(nil) 34 35 // NewKeyUpdaterAPI creates a new server-side keyupdater API end point. 36 func NewKeyUpdaterAPI( 37 st *state.State, 38 resources *common.Resources, 39 authorizer common.Authorizer, 40 ) (*KeyUpdaterAPI, error) { 41 // Only machine agents have access to the keyupdater service. 42 if !authorizer.AuthMachineAgent() { 43 return nil, common.ErrPerm 44 } 45 // No-one else except the machine itself can only read a machine's own credentials. 46 getCanRead := func() (common.AuthFunc, error) { 47 return authorizer.AuthOwner, nil 48 } 49 return &KeyUpdaterAPI{state: st, resources: resources, authorizer: authorizer, getCanRead: getCanRead}, nil 50 } 51 52 // WatchAuthorisedKeys starts a watcher to track changes to the authorised ssh keys 53 // for the specified machines. 54 // The current implementation relies on global authorised keys being stored in the environment config. 55 // This will change as new user management and authorisation functionality is added. 56 func (api *KeyUpdaterAPI) WatchAuthorisedKeys(arg params.Entities) (params.NotifyWatchResults, error) { 57 results := make([]params.NotifyWatchResult, len(arg.Entities)) 58 59 canRead, err := api.getCanRead() 60 if err != nil { 61 return params.NotifyWatchResults{}, mask(err) 62 } 63 for i, entity := range arg.Entities { 64 // 1. Check permissions 65 if !canRead(entity.Tag) { 66 results[i].Error = common.ServerError(common.ErrPerm) 67 continue 68 } 69 // 2. Check entity exists 70 if _, err := api.state.FindEntity(entity.Tag); err != nil { 71 if errors.IsNotFoundError(err) { 72 results[i].Error = common.ServerError(common.ErrPerm) 73 } else { 74 results[i].Error = common.ServerError(err) 75 } 76 continue 77 } 78 // 3. Watch fr changes 79 var err error 80 watch := api.state.WatchForEnvironConfigChanges() 81 // Consume the initial event. 82 if _, ok := <-watch.Changes(); ok { 83 results[i].NotifyWatcherId = api.resources.Register(watch) 84 } else { 85 err = watcher.MustErr(watch) 86 } 87 results[i].Error = common.ServerError(err) 88 } 89 return params.NotifyWatchResults{results}, nil 90 } 91 92 // AuthorisedKeys reports the authorised ssh keys for the specified machines. 93 // The current implementation relies on global authorised keys being stored in the environment config. 94 // This will change as new user management and authorisation functionality is added. 95 func (api *KeyUpdaterAPI) AuthorisedKeys(arg params.Entities) (params.StringsResults, error) { 96 if len(arg.Entities) == 0 { 97 return params.StringsResults{}, nil 98 } 99 results := make([]params.StringsResult, len(arg.Entities)) 100 101 // For now, authorised keys are global, common to all machines. 102 var keys []string 103 config, configErr := api.state.EnvironConfig() 104 if configErr == nil { 105 keys = ssh.SplitAuthorisedKeys(config.AuthorizedKeys()) 106 } 107 108 canRead, err := api.getCanRead() 109 if err != nil { 110 return params.StringsResults{}, mask(err) 111 } 112 for i, entity := range arg.Entities { 113 // 1. Check permissions 114 if !canRead(entity.Tag) { 115 results[i].Error = common.ServerError(common.ErrPerm) 116 continue 117 } 118 // 2. Check entity exists 119 if _, err := api.state.FindEntity(entity.Tag); err != nil { 120 if errors.IsNotFoundError(err) { 121 results[i].Error = common.ServerError(common.ErrPerm) 122 } else { 123 results[i].Error = common.ServerError(err) 124 } 125 continue 126 } 127 // 3. Get keys 128 var err error 129 if configErr == nil { 130 results[i].Result = keys 131 } else { 132 err = configErr 133 } 134 results[i].Error = common.ServerError(err) 135 } 136 return params.StringsResults{results}, nil 137 }