github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/apiserver/admin.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package apiserver
     5  
     6  import (
     7  	stderrors "errors"
     8  	"sync"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/names"
    12  	"github.com/juju/utils"
    13  
    14  	"github.com/juju/juju/rpc"
    15  	"github.com/juju/juju/state"
    16  	"github.com/juju/juju/state/api/params"
    17  	"github.com/juju/juju/state/apiserver/common"
    18  	"github.com/juju/juju/state/presence"
    19  )
    20  
    21  func newStateServer(srv *Server, rpcConn *rpc.Conn, reqNotifier *requestNotifier, limiter utils.Limiter) *initialRoot {
    22  	r := &initialRoot{
    23  		srv:     srv,
    24  		rpcConn: rpcConn,
    25  	}
    26  	r.admin = &srvAdmin{
    27  		root:        r,
    28  		limiter:     limiter,
    29  		reqNotifier: reqNotifier,
    30  	}
    31  	return r
    32  }
    33  
    34  // initialRoot implements the API that a client first sees
    35  // when connecting to the API. We start serving a different
    36  // API once the user has logged in.
    37  type initialRoot struct {
    38  	srv     *Server
    39  	rpcConn *rpc.Conn
    40  
    41  	admin *srvAdmin
    42  }
    43  
    44  // Admin returns an object that provides API access
    45  // to methods that can be called even when not
    46  // authenticated.
    47  func (r *initialRoot) Admin(id string) (*srvAdmin, error) {
    48  	if id != "" {
    49  		// Safeguard id for possible future use.
    50  		return nil, common.ErrBadId
    51  	}
    52  	return r.admin, nil
    53  }
    54  
    55  // srvAdmin is the only object that unlogged-in
    56  // clients can access. It holds any methods
    57  // that are needed to log in.
    58  type srvAdmin struct {
    59  	mu          sync.Mutex
    60  	limiter     utils.Limiter
    61  	root        *initialRoot
    62  	loggedIn    bool
    63  	reqNotifier *requestNotifier
    64  }
    65  
    66  var errAlreadyLoggedIn = stderrors.New("already logged in")
    67  
    68  // Login logs in with the provided credentials.
    69  // All subsequent requests on the connection will
    70  // act as the authenticated user.
    71  func (a *srvAdmin) Login(c params.Creds) (params.LoginResult, error) {
    72  	a.mu.Lock()
    73  	defer a.mu.Unlock()
    74  	if a.loggedIn {
    75  		// This can only happen if Login is called concurrently.
    76  		return params.LoginResult{}, errAlreadyLoggedIn
    77  	}
    78  	// Users are not rate limited, all other entities are
    79  	if kind, err := names.TagKind(c.AuthTag); err != nil || kind != names.UserTagKind {
    80  		if !a.limiter.Acquire() {
    81  			logger.Debugf("rate limiting, try again later")
    82  			return params.LoginResult{}, common.ErrTryAgain
    83  		}
    84  		defer a.limiter.Release()
    85  	}
    86  	entity, err := doCheckCreds(a.root.srv.state, c)
    87  	if err != nil {
    88  		return params.LoginResult{}, err
    89  	}
    90  	if a.reqNotifier != nil {
    91  		a.reqNotifier.login(entity.Tag())
    92  	}
    93  	// We have authenticated the user; now choose an appropriate API
    94  	// to serve to them.
    95  	// TODO: consider switching the new root based on who is logging in
    96  	newRoot := newSrvRoot(a.root, entity)
    97  	if err := a.startPingerIfAgent(newRoot, entity); err != nil {
    98  		return params.LoginResult{}, err
    99  	}
   100  
   101  	// Fetch the API server addresses from state.
   102  	hostPorts, err := a.root.srv.state.APIHostPorts()
   103  	if err != nil {
   104  		return params.LoginResult{}, err
   105  	}
   106  	logger.Debugf("hostPorts: %v", hostPorts)
   107  
   108  	environ, err := a.root.srv.state.Environment()
   109  	if err != nil {
   110  		return params.LoginResult{}, err
   111  	}
   112  
   113  	a.root.rpcConn.Serve(newRoot, serverError)
   114  	return params.LoginResult{
   115  		Servers:    hostPorts,
   116  		EnvironTag: environ.Tag(),
   117  	}, nil
   118  }
   119  
   120  var doCheckCreds = checkCreds
   121  
   122  func checkCreds(st *state.State, c params.Creds) (taggedAuthenticator, error) {
   123  	entity0, err := st.FindEntity(c.AuthTag)
   124  	if err != nil && !errors.IsNotFound(err) {
   125  		return nil, err
   126  	}
   127  	// We return the same error when an entity
   128  	// does not exist as for a bad password, so that
   129  	// we don't allow unauthenticated users to find information
   130  	// about existing entities.
   131  	entity, ok := entity0.(taggedAuthenticator)
   132  	if !ok {
   133  		return nil, common.ErrBadCreds
   134  	}
   135  	if err != nil || !entity.PasswordValid(c.Password) {
   136  		return nil, common.ErrBadCreds
   137  	}
   138  	// Check if a machine agent is logging in with the right Nonce
   139  	if err := checkForValidMachineAgent(entity, c); err != nil {
   140  		return nil, err
   141  	}
   142  	return entity, nil
   143  }
   144  
   145  func checkForValidMachineAgent(entity taggedAuthenticator, c params.Creds) error {
   146  	// If this is a machine agent connecting, we need to check the
   147  	// nonce matches, otherwise the wrong agent might be trying to
   148  	// connect.
   149  	if machine, ok := entity.(*state.Machine); ok {
   150  		if !machine.CheckProvisioned(c.Nonce) {
   151  			return state.NotProvisionedError(machine.Id())
   152  		}
   153  	}
   154  	return nil
   155  }
   156  
   157  // machinePinger wraps a presence.Pinger.
   158  type machinePinger struct {
   159  	*presence.Pinger
   160  }
   161  
   162  // Stop implements Pinger.Stop() as Pinger.Kill(), needed at
   163  // connection closing time to properly stop the wrapped pinger.
   164  func (p *machinePinger) Stop() error {
   165  	if err := p.Pinger.Stop(); err != nil {
   166  		return err
   167  	}
   168  	return p.Pinger.Kill()
   169  }
   170  
   171  func (a *srvAdmin) startPingerIfAgent(newRoot *srvRoot, entity taggedAuthenticator) error {
   172  	setAgentAliver, ok := entity.(interface {
   173  		SetAgentAlive() (*presence.Pinger, error)
   174  	})
   175  	if !ok {
   176  		return nil
   177  	}
   178  	// A machine or unit agent has connected, so start a pinger to
   179  	// announce it's now alive, and set up the API pinger
   180  	// so that the connection will be terminated if a sufficient
   181  	// interval passes between pings.
   182  	pinger, err := setAgentAliver.SetAgentAlive()
   183  	if err != nil {
   184  		return err
   185  	}
   186  	newRoot.resources.Register(&machinePinger{pinger})
   187  	action := func() {
   188  		if err := newRoot.rpcConn.Close(); err != nil {
   189  			logger.Errorf("error closing the RPC connection: %v", err)
   190  		}
   191  	}
   192  	pingTimeout := newPingTimeout(action, maxClientPingInterval)
   193  	newRoot.resources.RegisterNamed("pingTimeout", pingTimeout)
   194  	return nil
   195  }
   196  
   197  // errRoot implements the API that a client first sees
   198  // when connecting to the API. It exposes the same API as initialRoot, except
   199  // it returns the requested error when the client makes any request.
   200  type errRoot struct {
   201  	err error
   202  }
   203  
   204  // Admin conforms to the same API as initialRoot, but we'll always return (nil, err)
   205  func (r *errRoot) Admin(id string) (*srvAdmin, error) {
   206  	return nil, r.err
   207  }