github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/doc/logging-in-to-the-apiserver.txt (about)

     1  Logging in to the API Server
     2  ============================
     3  
     4  This document is being written because every time I need to look at how
     5  logging in to the API server works, I have to step through all the code and
     6  remind myself. Once I have done that, everything seems obvious and not worth
     7  writing down... until next time I need to think about the login process.
     8  
     9  Intended work is to add the ability to have a user log in to the root of the
    10  API server in order to get access to the environment manager and user manager
    11  facades. This allows the user to change their password, create environments
    12  and list the environments that they are able to connect to.
    13  
    14  The intended way that this is going to work is that the API client code will
    15  attempt to login to the server using a versioned login command. There is
    16  already a login-v1 that introduced new return values and a reauth request
    17  field. We will build on that behaviour to add a login-v2. If and only if the
    18  version 2 login is used the facades that are available at the apiserver will
    19  be restricted.  Logging in using version 1 or the original will continue to
    20  act as if you are logging into the state server environment - the original one
    21  and only environment that would exist before the Juju Environment Server work.
    22  
    23  This work then needs to look at the API connection workflow from both ends,
    24  the API client side, and the server side.
    25  
    26  apiserver.Server
    27  ----------------
    28  
    29  apiserver.go
    30  
    31  Server struct has a map of adminApiFactories. This currently has keys of
    32  0 and 1.  We will need to add a 2 here.
    33  
    34  The Server instance is created with NewServer, and listens on the API port.
    35  
    36  The incoming socket connection is handled inside the (*Server).run method.
    37   - "/environment/:envuuid/api" -> srv.apiHandler
    38   - "/" -> srv.apiHandler
    39  
    40  The apiHandler creates a go routine for handling the socket connection, and
    41  extracts the envuuid from the path, and calls the (*Server).serveConn method.
    42  
    43  serveConn validates the environment UUID and gets an appropiate *state.State
    44  for the environment specified.  An empty environment UUID is treated as the
    45  state server environment here.  One thing that will need to change is passing
    46  through whether or not we were given an environment UUID, as this is the last
    47  place that cares, but we will need to know later when the appropriate login
    48  method is called. The various admin API instances are created here and passed
    49  through to the newAnonRoot object.  This is passed in as the method finder for
    50  the websocket connection.
    51  
    52  root.go
    53  
    54  newAnonRoot only responds to calls on the "Admin" rootName.  The version of
    55  the FindMethod is used to determine whether the version 0 or 1 login API is
    56  used.
    57  
    58  The Login methods on the adminV0 and adminV1 instances call into the doLogin
    59  method.  It is here where we want to wrap the authedApi with something that
    60  limits the rootName objects that can be called.  In order to do this, we need
    61  to pass the envUUID from the path (remember this is either a valid uuid or the
    62  empty string), and the version of the login command used.
    63  
    64  In order to get the envuuid from the path into the Login method, we need to
    65  capture it in *Server.serveConn, where it is passed in as an argument.  We
    66  should pass it through to the newApiHandler function and store it on the
    67  apiHandler struct.  This handler is passed through as an arg to the
    68  newAnonRoot, and to the factory methods for the admin APIs.
    69  
    70  
    71  Implementation Notes:
    72  
    73  The login process for both v0 and v1 logins are handled in the method
    74  *admin.doLogin (in admin.go).  The method *Conn.ServeFinder on the *rpc.Conn
    75  attribute of the apiHandler is initially given the anonymous root here:
    76  
    77  ```go
    78  func (srv *Server) serveConn(wsConn *websocket.Conn, reqNotifier *requestNotifier, envUUID string) error {
    79  	// ...
    80  		conn.ServeFinder(newAnonRoot(h, adminApis), serverError)
    81  	// ...
    82  ```
    83  
    84  If login is successful, the doLogin method changes out the method finder that
    85  is used for the RPC connection just before the end of the return statement at
    86  the end of the method.
    87  
    88  ```go
    89  	// a *admin
    90  	//
    91  	// root is `*apiHander` from the admin struct.
    92  	a.root.rpcConn.ServeFinder(authedApi, serverError)
    93  ```
    94  
    95  In order to supply a restricted api at the root, the doLogin method will need
    96  to take a version number.  Just before we replace the method finder for the
    97  RPC connection, we check the version number and the envUUID of the api
    98  handler, and wrap the authedApi in a restricted root method finder implemented
    99  in a similar manner to the upgradingRoot method finder.
   100