|
5 | 5 | package lfs
|
6 | 6 |
|
7 | 7 | import (
|
| 8 | + "crypto/sha256" |
8 | 9 | "encoding/base64"
|
| 10 | + "encoding/hex" |
9 | 11 | "errors"
|
10 | 12 | "fmt"
|
11 | 13 | "io"
|
@@ -213,14 +215,22 @@ func BatchHandler(ctx *context.Context) {
|
213 | 215 | }
|
214 | 216 | }
|
215 | 217 |
|
216 |
| - if exists { |
217 |
| - if meta == nil { |
| 218 | + if exists && meta == nil { |
| 219 | + accessible, err := models.LFSObjectAccessible(ctx.User, p.Oid) |
| 220 | + if err != nil { |
| 221 | + log.Error("Unable to check if LFS MetaObject [%s] is accessible. Error: %v", p.Oid, err) |
| 222 | + writeStatus(ctx, http.StatusInternalServerError) |
| 223 | + return |
| 224 | + } |
| 225 | + if accessible { |
218 | 226 | _, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: p, RepositoryID: repository.ID})
|
219 | 227 | if err != nil {
|
220 | 228 | log.Error("Unable to create LFS MetaObject [%s] for %s/%s. Error: %v", p.Oid, rc.User, rc.Repo, err)
|
221 | 229 | writeStatus(ctx, http.StatusInternalServerError)
|
222 | 230 | return
|
223 | 231 | }
|
| 232 | + } else { |
| 233 | + exists = false |
224 | 234 | }
|
225 | 235 | }
|
226 | 236 |
|
@@ -270,29 +280,50 @@ func UploadHandler(ctx *context.Context) {
|
270 | 280 | return
|
271 | 281 | }
|
272 | 282 |
|
273 |
| - meta, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: p, RepositoryID: repository.ID}) |
274 |
| - if err != nil { |
275 |
| - log.Error("Unable to create LFS MetaObject [%s] for %s/%s. Error: %v", p.Oid, rc.User, rc.Repo, err) |
276 |
| - writeStatus(ctx, http.StatusInternalServerError) |
277 |
| - return |
278 |
| - } |
279 |
| - |
280 | 283 | contentStore := lfs_module.NewContentStore()
|
281 |
| - |
282 | 284 | exists, err := contentStore.Exists(p)
|
283 | 285 | if err != nil {
|
284 | 286 | log.Error("Unable to check if LFS OID[%s] exist. Error: %v", p.Oid, err)
|
285 | 287 | writeStatus(ctx, http.StatusInternalServerError)
|
286 | 288 | return
|
287 | 289 | }
|
288 |
| - if meta.Existing || exists { |
289 |
| - ctx.Resp.WriteHeader(http.StatusOK) |
290 |
| - return |
| 290 | + |
| 291 | + uploadOrVerify := func() error { |
| 292 | + if exists { |
| 293 | + accessible, err := models.LFSObjectAccessible(ctx.User, p.Oid) |
| 294 | + if err != nil { |
| 295 | + log.Error("Unable to check if LFS MetaObject [%s] is accessible. Error: %v", p.Oid, err) |
| 296 | + return err |
| 297 | + } |
| 298 | + if !accessible { |
| 299 | + // The file exists but the user has no access to it. |
| 300 | + // The upload gets verified by hashing and size comparison to prove access to it. |
| 301 | + hash := sha256.New() |
| 302 | + written, err := io.Copy(hash, ctx.Req.Body) |
| 303 | + if err != nil { |
| 304 | + log.Error("Error creating hash. Error: %v", err) |
| 305 | + return err |
| 306 | + } |
| 307 | + |
| 308 | + if written != p.Size { |
| 309 | + return lfs_module.ErrSizeMismatch |
| 310 | + } |
| 311 | + if hex.EncodeToString(hash.Sum(nil)) != p.Oid { |
| 312 | + return lfs_module.ErrHashMismatch |
| 313 | + } |
| 314 | + } |
| 315 | + } else if err := contentStore.Put(p, ctx.Req.Body); err != nil { |
| 316 | + log.Error("Error putting LFS MetaObject [%s] into content store. Error: %v", p.Oid, err) |
| 317 | + return err |
| 318 | + } |
| 319 | + _, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: p, RepositoryID: repository.ID}) |
| 320 | + return err |
291 | 321 | }
|
292 | 322 |
|
293 | 323 | defer ctx.Req.Body.Close()
|
294 |
| - if err := contentStore.Put(meta.Pointer, ctx.Req.Body); err != nil { |
| 324 | + if err := uploadOrVerify(); err != nil { |
295 | 325 | if errors.Is(err, lfs_module.ErrSizeMismatch) || errors.Is(err, lfs_module.ErrHashMismatch) {
|
| 326 | + log.Error("Upload does not match LFS MetaObject [%s]. Error: %v", p.Oid, err) |
296 | 327 | writeStatusMessage(ctx, http.StatusUnprocessableEntity, err.Error())
|
297 | 328 | } else {
|
298 | 329 | writeStatus(ctx, http.StatusInternalServerError)
|
|
0 commit comments