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  }