github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/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  	"launchpad.net/juju-core/errors"
    11  	"launchpad.net/juju-core/rpc"
    12  	"launchpad.net/juju-core/state"
    13  	"launchpad.net/juju-core/state/api/params"
    14  	"launchpad.net/juju-core/state/apiserver/common"
    15  	"launchpad.net/juju-core/state/presence"
    16  )
    17  
    18  func newStateServer(srv *Server, rpcConn *rpc.Conn, reqNotifier *requestNotifier) *initialRoot {
    19  	r := &initialRoot{
    20  		srv:     srv,
    21  		rpcConn: rpcConn,
    22  	}
    23  	r.admin = &srvAdmin{
    24  		root:        r,
    25  		reqNotifier: reqNotifier,
    26  	}
    27  	return r
    28  }
    29  
    30  // initialRoot implements the API that a client first sees
    31  // when connecting to the API. We start serving a different
    32  // API once the user has logged in.
    33  type initialRoot struct {
    34  	srv     *Server
    35  	rpcConn *rpc.Conn
    36  
    37  	admin *srvAdmin
    38  }
    39  
    40  // Admin returns an object that provides API access
    41  // to methods that can be called even when not
    42  // authenticated.
    43  func (r *initialRoot) Admin(id string) (*srvAdmin, error) {
    44  	if id != "" {
    45  		// Safeguard id for possible future use.
    46  		return nil, common.ErrBadId
    47  	}
    48  	return r.admin, nil
    49  }
    50  
    51  // srvAdmin is the only object that unlogged-in
    52  // clients can access. It holds any methods
    53  // that are needed to log in.
    54  type srvAdmin struct {
    55  	mu          sync.Mutex
    56  	root        *initialRoot
    57  	loggedIn    bool
    58  	reqNotifier *requestNotifier
    59  }
    60  
    61  var errAlreadyLoggedIn = stderrors.New("already logged in")
    62  
    63  // Login logs in with the provided credentials.
    64  // All subsequent requests on the connection will
    65  // act as the authenticated user.
    66  func (a *srvAdmin) Login(c params.Creds) error {
    67  	a.mu.Lock()
    68  	defer a.mu.Unlock()
    69  	if a.loggedIn {
    70  		// This can only happen if Login is called concurrently.
    71  		return errAlreadyLoggedIn
    72  	}
    73  	entity, err := checkCreds(a.root.srv.state, c)
    74  	if err != nil {
    75  		return err
    76  	}
    77  	if a.reqNotifier != nil {
    78  		a.reqNotifier.login(entity.Tag())
    79  	}
    80  	// We have authenticated the user; now choose an appropriate API
    81  	// to serve to them.
    82  	newRoot, err := a.apiRootForEntity(entity, c)
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	a.root.rpcConn.Serve(newRoot, serverError)
    88  	return nil
    89  }
    90  
    91  func checkCreds(st *state.State, c params.Creds) (taggedAuthenticator, error) {
    92  	entity0, err := st.FindEntity(c.AuthTag)
    93  	if err != nil && !errors.IsNotFoundError(err) {
    94  		return nil, err
    95  	}
    96  	// We return the same error when an entity
    97  	// does not exist as for a bad password, so that
    98  	// we don't allow unauthenticated users to find information
    99  	// about existing entities.
   100  	entity, ok := entity0.(taggedAuthenticator)
   101  	if !ok {
   102  		return nil, common.ErrBadCreds
   103  	}
   104  	if err != nil || !entity.PasswordValid(c.Password) {
   105  		return nil, common.ErrBadCreds
   106  	}
   107  	return entity, nil
   108  }
   109  
   110  // machinePinger wraps a presence.Pinger.
   111  type machinePinger struct {
   112  	*presence.Pinger
   113  }
   114  
   115  // Stop implements Pinger.Stop() as Pinger.Kill(), needed at
   116  // connection closing time to properly stop the wrapped pinger.
   117  func (p *machinePinger) Stop() error {
   118  	if err := p.Pinger.Stop(); err != nil {
   119  		return err
   120  	}
   121  	return p.Pinger.Kill()
   122  }
   123  
   124  func (a *srvAdmin) apiRootForEntity(entity taggedAuthenticator, c params.Creds) (interface{}, error) {
   125  	// TODO(rog) choose appropriate object to serve.
   126  	newRoot := newSrvRoot(a.root, entity)
   127  
   128  	// If this is a machine agent connecting, we need to check the
   129  	// nonce matches, otherwise the wrong agent might be trying to
   130  	// connect.
   131  	machine, ok := entity.(*state.Machine)
   132  	if ok {
   133  		if !machine.CheckProvisioned(c.Nonce) {
   134  			return nil, state.NotProvisionedError(machine.Id())
   135  		}
   136  	}
   137  	setAgentAliver, ok := entity.(interface {
   138  		SetAgentAlive() (*presence.Pinger, error)
   139  	})
   140  	if ok {
   141  		// A machine or unit agent has connected, so start a pinger to
   142  		// announce it's now alive, and set up the API pinger
   143  		// so that the connection will be terminated if a sufficient
   144  		// interval passes between pings.
   145  		pinger, err := setAgentAliver.SetAgentAlive()
   146  		if err != nil {
   147  			return nil, err
   148  		}
   149  		newRoot.resources.Register(&machinePinger{pinger})
   150  		action := func() {
   151  			if err := newRoot.rpcConn.Close(); err != nil {
   152  				logger.Errorf("error closing the RPC connection: %v", err)
   153  			}
   154  		}
   155  		newRoot.pingTimeout = newPingTimeout(action, maxPingInterval)
   156  	}
   157  	return newRoot, nil
   158  }