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