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 }