private string GenerateUploadVerifyToken(LFSRequest.LFSObject obj) { var token = new UploadVerifyToken() { Oid = obj.Oid, Size = obj.Size }; var value = JsonSerializer.Serialize(token); return(dataProtector.Protect(value, UploadTokenValidTime)); }
private async ValueTask <LFSResponse.LFSObject> HandleUpload(LfsProject project, LFSRequest.LFSObject obj) { var existingObject = await database.LfsObjects .Where(o => o.LfsOid == obj.Oid && o.LfsProjectId == project.Id).Include(o => o.LfsProject) .FirstOrDefaultAsync(); if (existingObject != null) { // We already have this object return(new LFSResponse.LFSObject(obj.Oid, obj.Size) { Actions = null, Authenticated = null }); } if (obj.Size > AppInfo.MaxLfsUploadSize) { return(new LFSResponse.LFSObject(obj.Oid, obj.Size, new LFSResponse.LFSObject.ErrorInfo(StatusCodes.Status422UnprocessableEntity, "File is too large"))); } logger.LogTrace("Requesting auth because new object is to be uploaded {Oid} for project {Name}", obj.Oid, project.Name); // New object. User must have write access if (!RequireWriteAccess(out var result)) { throw new InvalidAccessException(result !); } // We don't yet create the LfsObject here to guard against upload failures // instead the verify callback does that // The uploads prefix is used here to ensure the user can't overwrite the file after uploading and // verification var storagePath = "uploads/" + LfsDownloadUrls.OidStoragePath(project, obj.Oid); if (!bucketChecked) { try { if (!await remoteStorage.BucketExists()) { throw new Exception("bucket doesn't exist"); } } catch (Exception e) { logger.LogWarning("Bucket check failed: {@E}", e); var error = "remote storage is inaccessible"; throw new HttpResponseException() { Status = StatusCodes.Status500InternalServerError, ContentType = AppInfo.GitLfsContentType, Value = new GitLFSErrorResponse() { Message = error }.ToString() }; } bucketChecked = true; } var verifyUrl = QueryHelpers.AddQueryString( new Uri(configuration.GetBaseUrl(), $"api/v1/lfs/{project.Slug}/verify").ToString(), "token", GenerateUploadVerifyToken(obj)); return(new LFSResponse.LFSObject(obj.Oid, obj.Size) { Actions = new Dictionary <string, LFSResponse.LFSObject.Action>() { { "upload", new LFSResponse.LFSObject.UploadAction() { Href = remoteStorage.CreatePresignedUploadURL(storagePath, S3UploadValidTime), ExpiresIn = (int)UploadValidTime.TotalSeconds } }, { "verify", new LFSResponse.LFSObject.UploadAction() { Href = verifyUrl, ExpiresIn = (int)UploadValidTime.TotalSeconds } } } }); }
private async ValueTask <LFSResponse.LFSObject> HandleDownload(LfsProject project, LFSRequest.LFSObject obj) { var existingObject = await database.LfsObjects .Where(o => o.LfsOid == obj.Oid && o.LfsProjectId == project.Id).Include(o => o.LfsProject) .FirstOrDefaultAsync(); if (existingObject == null) { logger.LogWarning("Non-existing OID requested: {Oid}", obj.Oid); return(new LFSResponse.LFSObject(obj.Oid, obj.Size, new LFSResponse.LFSObject.ErrorInfo(StatusCodes.Status404NotFound, "OID not found"))); } var createdUrl = downloadUrls.CreateDownloadFor(existingObject, DownloadUrlExpireTime); return(new LFSResponse.LFSObject(obj.Oid, obj.Size) { Actions = new Dictionary <string, LFSResponse.LFSObject.Action>() { { "download", new LFSResponse.LFSObject.DownloadAction() { Href = createdUrl, ExpiresIn = (int)DownloadExpireTime.TotalSeconds } } } }); }