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