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