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  }