github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/api/auth/check_build_read_access_handler.go (about)

     1  package auth
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net/http"
     7  	"strconv"
     8  
     9  	"github.com/pf-qiu/concourse/v6/atc/api/accessor"
    10  	"github.com/pf-qiu/concourse/v6/atc/db"
    11  )
    12  
    13  type CheckBuildReadAccessHandlerFactory interface {
    14  	AnyJobHandler(delegateHandler http.Handler, rejector Rejector) http.Handler
    15  	CheckIfPrivateJobHandler(delegateHandler http.Handler, rejector Rejector) http.Handler
    16  }
    17  
    18  type checkBuildReadAccessHandlerFactory struct {
    19  	buildFactory db.BuildFactory
    20  }
    21  
    22  func NewCheckBuildReadAccessHandlerFactory(
    23  	buildFactory db.BuildFactory,
    24  ) *checkBuildReadAccessHandlerFactory {
    25  	return &checkBuildReadAccessHandlerFactory{
    26  		buildFactory: buildFactory,
    27  	}
    28  }
    29  
    30  func (f *checkBuildReadAccessHandlerFactory) AnyJobHandler(
    31  	delegateHandler http.Handler,
    32  	rejector Rejector,
    33  ) http.Handler {
    34  	return checkBuildReadAccessHandler{
    35  		rejector:        rejector,
    36  		buildFactory:    f.buildFactory,
    37  		delegateHandler: delegateHandler,
    38  		allowPrivateJob: true,
    39  	}
    40  }
    41  
    42  func (f *checkBuildReadAccessHandlerFactory) CheckIfPrivateJobHandler(
    43  	delegateHandler http.Handler,
    44  	rejector Rejector,
    45  ) http.Handler {
    46  	return checkBuildReadAccessHandler{
    47  		rejector:        rejector,
    48  		buildFactory:    f.buildFactory,
    49  		delegateHandler: delegateHandler,
    50  		allowPrivateJob: false,
    51  	}
    52  }
    53  
    54  type checkBuildReadAccessHandler struct {
    55  	rejector        Rejector
    56  	buildFactory    db.BuildFactory
    57  	delegateHandler http.Handler
    58  	allowPrivateJob bool
    59  }
    60  
    61  func (h checkBuildReadAccessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    62  	buildIDStr := r.FormValue(":build_id")
    63  	buildID, err := strconv.Atoi(buildIDStr)
    64  	if err != nil {
    65  		w.WriteHeader(http.StatusBadRequest)
    66  		return
    67  	}
    68  
    69  	build, found, err := h.buildFactory.Build(buildID)
    70  	if err != nil {
    71  		w.WriteHeader(http.StatusInternalServerError)
    72  		return
    73  	}
    74  
    75  	if !found {
    76  		w.WriteHeader(http.StatusNotFound)
    77  		return
    78  	}
    79  
    80  	acc := accessor.GetAccessor(r)
    81  
    82  	allow, err := h.allow(build, acc)
    83  	if err != nil {
    84  		if err == errDisappeared {
    85  			w.WriteHeader(http.StatusNotFound)
    86  		} else {
    87  			w.WriteHeader(http.StatusInternalServerError)
    88  		}
    89  		return
    90  	}
    91  
    92  	if allow {
    93  		ctx := context.WithValue(r.Context(), BuildContextKey, build)
    94  		h.delegateHandler.ServeHTTP(w, r.WithContext(ctx))
    95  	} else if acc.IsAuthenticated() {
    96  		h.rejector.Forbidden(w, r)
    97  	} else {
    98  		h.rejector.Unauthorized(w, r)
    99  	}
   100  }
   101  
   102  // this is mainly to avoid a monstrosity like bool, bool, error; it's handled
   103  // above
   104  var errDisappeared = errors.New("internal: build parent disappeared")
   105  
   106  func (h checkBuildReadAccessHandler) allow(build db.Build, acc accessor.Access) (bool, error) {
   107  	if acc.IsAuthenticated() && acc.IsAuthorized(build.TeamName()) {
   108  		return true, nil
   109  	}
   110  
   111  	if build.PipelineID() == 0 {
   112  		return false, nil
   113  	}
   114  
   115  	pipeline, found, err := build.Pipeline()
   116  	if err != nil {
   117  		return false, err
   118  	}
   119  
   120  	if !found {
   121  		return false, errDisappeared
   122  	}
   123  
   124  	if !pipeline.Public() {
   125  		return false, nil
   126  	}
   127  
   128  	if h.allowPrivateJob {
   129  		return true, nil
   130  	}
   131  
   132  	if build.JobID() == 0 {
   133  		return false, nil
   134  	}
   135  
   136  	job, found, err := pipeline.Job(build.JobName())
   137  	if err != nil {
   138  		return false, err
   139  	}
   140  
   141  	if !found {
   142  		return false, errDisappeared
   143  	}
   144  
   145  	if job.Public() {
   146  		return true, nil
   147  	}
   148  
   149  	return false, nil
   150  }