github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/apiserver/httphandler.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 "encoding/base64" 8 "fmt" 9 "net/http" 10 "strings" 11 12 "github.com/juju/names" 13 14 "github.com/juju/juju/state" 15 "github.com/juju/juju/state/api/params" 16 "github.com/juju/juju/state/apiserver/common" 17 ) 18 19 // errorSender implementations send errors back to the caller. 20 type errorSender interface { 21 sendError(w http.ResponseWriter, statusCode int, message string) error 22 } 23 24 // httpHandler handles http requests through HTTPS in the API server. 25 type httpHandler struct { 26 state *state.State 27 } 28 29 // authenticate parses HTTP basic authentication and authorizes the 30 // request by looking up the provided tag and password against state. 31 func (h *httpHandler) authenticate(r *http.Request) error { 32 parts := strings.Fields(r.Header.Get("Authorization")) 33 if len(parts) != 2 || parts[0] != "Basic" { 34 // Invalid header format or no header provided. 35 return fmt.Errorf("invalid request format") 36 } 37 // Challenge is a base64-encoded "tag:pass" string. 38 // See RFC 2617, Section 2. 39 challenge, err := base64.StdEncoding.DecodeString(parts[1]) 40 if err != nil { 41 return fmt.Errorf("invalid request format") 42 } 43 tagPass := strings.SplitN(string(challenge), ":", 2) 44 if len(tagPass) != 2 { 45 return fmt.Errorf("invalid request format") 46 } 47 // Only allow users, not agents. 48 _, _, err = names.ParseTag(tagPass[0], names.UserTagKind) 49 if err != nil { 50 return common.ErrBadCreds 51 } 52 // Ensure the credentials are correct. 53 _, err = checkCreds(h.state, params.Creds{ 54 AuthTag: tagPass[0], 55 Password: tagPass[1], 56 }) 57 return err 58 } 59 60 func (h *httpHandler) getEnvironUUID(r *http.Request) string { 61 return r.URL.Query().Get(":envuuid") 62 } 63 64 func (h *httpHandler) validateEnvironUUID(r *http.Request) error { 65 // Note: this is only true until we have support for multiple 66 // environments. For now, there is only one, so we make sure that is 67 // the one being addressed. 68 envUUID := h.getEnvironUUID(r) 69 logger.Tracef("got a request for env %q", envUUID) 70 if envUUID == "" { 71 return nil 72 } 73 env, err := h.state.Environment() 74 if err != nil { 75 logger.Infof("error looking up environment: %v", err) 76 return err 77 } 78 if env.UUID() != envUUID { 79 logger.Infof("environment uuid mismatch: %v != %v", 80 envUUID, env.UUID()) 81 return common.UnknownEnvironmentError(envUUID) 82 } 83 return nil 84 } 85 86 // authError sends an unauthorized error. 87 func (h *httpHandler) authError(w http.ResponseWriter, sender errorSender) { 88 w.Header().Set("WWW-Authenticate", `Basic realm="juju"`) 89 sender.sendError(w, http.StatusUnauthorized, "unauthorized") 90 }