forked from tangled.org/core
Monorepo for Tangled

appview: relax auth requirement on artifact download

Signed-off-by: oppiliappan <me@oppi.li>

authored by oppi.li and committed by Tangled 3d80614e dd94ba23

Changed files
+42 -17
appview
+40 -14
appview/repo/artifact.go
··· 4 4 "context" 5 5 "encoding/json" 6 6 "fmt" 7 + "io" 7 8 "log" 8 9 "net/http" 9 10 "net/url" ··· 134 135 }) 135 136 } 136 137 137 - // TODO: proper statuses here on early exit 138 138 func (rp *Repo) DownloadArtifact(w http.ResponseWriter, r *http.Request) { 139 - tagParam := chi.URLParam(r, "tag") 140 - filename := chi.URLParam(r, "file") 141 139 f, err := rp.repoResolver.Resolve(r) 142 140 if err != nil { 143 141 log.Println("failed to get repo and knot", err) 142 + http.Error(w, "failed to resolve repo", http.StatusInternalServerError) 144 143 return 145 144 } 145 + 146 + tagParam := chi.URLParam(r, "tag") 147 + filename := chi.URLParam(r, "file") 146 148 147 149 tag, err := rp.resolveTag(r.Context(), f, tagParam) 148 150 if err != nil { ··· 151 153 return 152 154 } 153 155 154 - client, err := rp.oauth.AuthorizedClient(r) 155 - if err != nil { 156 - log.Println("failed to get authorized client", err) 157 - return 158 - } 159 - 160 156 artifacts, err := db.GetArtifact( 161 157 rp.db, 162 158 db.FilterEq("repo_at", f.RepoAt()), ··· 165 161 ) 166 162 if err != nil { 167 163 log.Println("failed to get artifacts", err) 164 + http.Error(w, "failed to get artifact", http.StatusInternalServerError) 168 165 return 169 166 } 167 + 170 168 if len(artifacts) != 1 { 171 - log.Printf("too many or too little artifacts found") 169 + log.Printf("too many or too few artifacts found") 170 + http.Error(w, "artifact not found", http.StatusNotFound) 172 171 return 173 172 } 174 173 175 174 artifact := artifacts[0] 176 175 177 - getBlobResp, err := client.SyncGetBlob(r.Context(), artifact.BlobCid.String(), artifact.Did) 176 + ownerPds := f.OwnerId.PDSEndpoint() 177 + url, _ := url.Parse(fmt.Sprintf("%s/xrpc/com.atproto.sync.getBlob", ownerPds)) 178 + q := url.Query() 179 + q.Set("cid", artifact.BlobCid.String()) 180 + q.Set("did", artifact.Did) 181 + url.RawQuery = q.Encode() 182 + 183 + req, err := http.NewRequest(http.MethodGet, url.String(), nil) 184 + if err != nil { 185 + log.Println("failed to create request", err) 186 + http.Error(w, "failed to create request", http.StatusInternalServerError) 187 + return 188 + } 189 + req.Header.Set("Content-Type", "application/json") 190 + 191 + resp, err := http.DefaultClient.Do(req) 178 192 if err != nil { 179 - log.Println("failed to get blob from pds", err) 193 + log.Println("failed to make request", err) 194 + http.Error(w, "failed to make request to PDS", http.StatusInternalServerError) 180 195 return 181 196 } 197 + defer resp.Body.Close() 182 198 183 - w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", filename)) 184 - w.Write(getBlobResp) 199 + // copy status code and relevant headers from upstream response 200 + w.WriteHeader(resp.StatusCode) 201 + for key, values := range resp.Header { 202 + for _, v := range values { 203 + w.Header().Add(key, v) 204 + } 205 + } 206 + 207 + // stream the body directly to the client 208 + if _, err := io.Copy(w, resp.Body); err != nil { 209 + log.Println("error streaming response to client:", err) 210 + } 185 211 } 186 212 187 213 // TODO: proper statuses here on early exit
+2 -3
appview/repo/router.go
··· 21 21 r.Route("/tags", func(r chi.Router) { 22 22 r.Get("/", rp.RepoTags) 23 23 r.Route("/{tag}", func(r chi.Router) { 24 - r.Use(middleware.AuthMiddleware(rp.oauth)) 25 - // require auth to download for now 26 24 r.Get("/download/{file}", rp.DownloadArtifact) 27 25 28 26 // require repo:push to upload or delete artifacts ··· 30 28 // additionally: only the uploader can truly delete an artifact 31 29 // (record+blob will live on their pds) 32 30 r.Group(func(r chi.Router) { 33 - r.With(mw.RepoPermissionMiddleware("repo:push")) 31 + r.Use(middleware.AuthMiddleware(rp.oauth)) 32 + r.Use(mw.RepoPermissionMiddleware("repo:push")) 34 33 r.Post("/upload", rp.AttachArtifact) 35 34 r.Delete("/{file}", rp.DeleteArtifact) 36 35 })