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  }