github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/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 if _, err := names.ParseTag(tagPass[0], names.UserTagKind); err != nil { 49 return common.ErrBadCreds 50 } 51 // Ensure the credentials are correct. 52 _, err = checkCreds(h.state, params.Creds{ 53 AuthTag: tagPass[0], 54 Password: tagPass[1], 55 }) 56 return err 57 } 58 59 func (h *httpHandler) getEnvironUUID(r *http.Request) string { 60 return r.URL.Query().Get(":envuuid") 61 } 62 63 func (h *httpHandler) validateEnvironUUID(r *http.Request) error { 64 // Note: this is only true until we have support for multiple 65 // environments. For now, there is only one, so we make sure that is 66 // the one being addressed. 67 envUUID := h.getEnvironUUID(r) 68 logger.Tracef("got a request for env %q", envUUID) 69 if envUUID == "" { 70 return nil 71 } 72 env, err := h.state.Environment() 73 if err != nil { 74 logger.Infof("error looking up environment: %v", err) 75 return err 76 } 77 if env.UUID() != envUUID { 78 logger.Infof("environment uuid mismatch: %v != %v", 79 envUUID, env.UUID()) 80 return common.UnknownEnvironmentError(envUUID) 81 } 82 return nil 83 } 84 85 // authError sends an unauthorized error. 86 func (h *httpHandler) authError(w http.ResponseWriter, sender errorSender) { 87 w.Header().Set("WWW-Authenticate", `Basic realm="juju"`) 88 sender.sendError(w, http.StatusUnauthorized, "unauthorized") 89 }