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  }