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 }