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  }