github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/apiv3/route/prefetch.go (about)

     1  package route
     2  
     3  import (
     4  	"net/http"
     5  
     6  	"github.com/evergreen-ci/evergreen/apiv3"
     7  	"github.com/evergreen-ci/evergreen/apiv3/servicecontext"
     8  	"github.com/evergreen-ci/evergreen/model"
     9  	"github.com/evergreen-ci/evergreen/model/user"
    10  	"github.com/gorilla/context"
    11  	"github.com/gorilla/mux"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  type (
    16  	// custom types used to attach specific values to request contexts, to prevent collisions.
    17  	requestUserKey    int
    18  	requestContextKey int
    19  )
    20  
    21  const (
    22  	// Key values used to map user and project data to request context.
    23  	// These are private custom types to avoid key collisions.
    24  	RequestUser    requestUserKey    = 0
    25  	RequestContext requestContextKey = 0
    26  )
    27  
    28  // PrefetchFunc is a function signature that defines types of functions which may
    29  // be used to fetch data before the main request handler is called. They should
    30  // fetch data using the ServiceeContext and set them on the request context.
    31  type PrefetchFunc func(*http.Request, servicecontext.ServiceContext) error
    32  
    33  // PrefetchUser gets the user information from a request, and uses it to
    34  // get the associated user from the database and attaches it to the request context.
    35  func PrefetchUser(r *http.Request, sc servicecontext.ServiceContext) error {
    36  	// Grab API auth details from header
    37  	var authDataAPIKey, authDataName string
    38  
    39  	if len(r.Header["Api-Key"]) > 0 {
    40  		authDataAPIKey = r.Header["Api-Key"][0]
    41  	}
    42  	if len(r.Header["Auth-Username"]) > 0 {
    43  		authDataName = r.Header["Auth-Username"][0]
    44  	}
    45  	if len(authDataName) == 0 && len(r.Header["Api-User"]) > 0 {
    46  		authDataName = r.Header["Api-User"][0]
    47  	}
    48  
    49  	if len(authDataAPIKey) > 0 {
    50  		apiUser, err := sc.FindUserById(authDataName)
    51  		if apiUser.(*user.DBUser) != nil && err == nil {
    52  			if apiUser.GetAPIKey() != authDataAPIKey {
    53  				return apiv3.APIError{
    54  					StatusCode: http.StatusUnauthorized,
    55  					Message:    "Invalid API key",
    56  				}
    57  			}
    58  			context.Set(r, RequestUser, apiUser)
    59  		}
    60  	}
    61  	return nil
    62  }
    63  
    64  // PrefetchProjectContext gets the information related to the project that the request contains
    65  // and fetches the associated project context and attaches that to the request context.
    66  func PrefetchProjectContext(r *http.Request, sc servicecontext.ServiceContext) error {
    67  	vars := mux.Vars(r)
    68  	taskId := vars["task_id"]
    69  	buildId := vars["build_id"]
    70  	versionId := vars["version_id"]
    71  	patchId := vars["patch_id"]
    72  	projectId := vars["project_id"]
    73  	ctx, err := sc.FetchContext(taskId, buildId, versionId, patchId, projectId)
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	if ctx.ProjectRef != nil && ctx.ProjectRef.Private && GetUser(r) == nil {
    79  		// Project is private and user is not authorized so return not found
    80  		return apiv3.APIError{
    81  			StatusCode: http.StatusNotFound,
    82  			Message:    "Project not found",
    83  		}
    84  	}
    85  
    86  	if ctx.Patch != nil && GetUser(r) == nil {
    87  		return apiv3.APIError{
    88  			StatusCode: http.StatusNotFound,
    89  			Message:    "Not found",
    90  		}
    91  	}
    92  	context.Set(r, RequestContext, &ctx)
    93  	return nil
    94  }
    95  
    96  // GetUser returns the user associated with a given http request.
    97  func GetUser(r *http.Request) *user.DBUser {
    98  	if rv := context.Get(r, RequestUser); rv != nil {
    99  		return rv.(*user.DBUser)
   100  	}
   101  	return nil
   102  }
   103  
   104  // GetProjectContext returns the project context associated with a
   105  // given request.
   106  func GetProjectContext(r *http.Request) (*model.Context, error) {
   107  	if rv := context.Get(r, RequestContext); rv != nil {
   108  		return rv.(*model.Context), nil
   109  	}
   110  	return &model.Context{}, errors.New("No context loaded")
   111  }
   112  
   113  // MustHaveProjectContext returns the project context set on the
   114  // http request context. It panics if none is set.
   115  func MustHaveProjectContext(r *http.Request) *model.Context {
   116  	pc, err := GetProjectContext(r)
   117  	if err != nil {
   118  		panic(err)
   119  	}
   120  	return pc
   121  }
   122  
   123  // MustHaveUser returns the user associated with a given request or panics
   124  // if none is present.
   125  func MustHaveUser(r *http.Request) *user.DBUser {
   126  	u := GetUser(r)
   127  	if u == nil {
   128  		panic("no user attached to request")
   129  	}
   130  	return u
   131  }