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