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 }