NotesWhat is notes.io?

Notes brand slogan

Notes - notes.io

Accessibility information
This page has four areas: A sidebar with information about the challenge, a tree view “File Explorer” containing the project files, a “Code blocks” section with a list of code blocks relevant to the challenge, and a code viewer section. The code viewer section has two groups of toggle buttons at the top, to select and compare the vulnerable code with possible solutions. One group selects the code comparison base and the other selects a solution to submit as an answer. Below those are tabs displaying opened files, with the currently opened file presented in a table. The presentation of the code viewer table depends on the ‘inline diff view’ option under editor settings. If unchecked, the table has 5 columns: Code block indicator, two for line number and code of the selected comparison base, and two for the line number and code of the selected solution variation. If ‘inline diff’ is checked, the table has 4 columns: Code block indicator, line number for the code from the comparison base, line number for the code from the selected solution, and a column with the code itself. In both presentations the code block indicator is only present in the first line of a code block and only if ‘vulnerable code’ is the current comparison base. Each line of code within a code block is prefixed with ‘CBX’, where X is a number used to distinguish between code blocks. Deleted code lines are prefixed with a ‘-’ symbol, and added lines with a ‘+’ symbol. Opening category information, pressing the hints or submit buttons will open a modal dialog.

Skip to Code Editor
Identify the vulnerability
Fix the vulnerability
Fix the vulnerability
Find and select the solution that fixes the vulnerability listed below. Each solution may take a different approach to address the problem, but only one solution is correct.

Vulnerability Category
Access Control - Missing Object Level Access Control

Actions
Attempts left: 1

View shortcuts keyboard hotkey:?

File Explorer
Highlighted files only: 85 files hidden


Services3 code blocks
Domain3 code blocks
*ReviewAttachmentService.cs3 code blocks
3
Code blocks
ReviewAttachmentService.cs:48-55
ReviewAttachmentService.cs:61-82
ReviewAttachmentService.cs:102-121
Comparison baseComparison base
Selected solutionSelected solution
using ProductsCatalog.Extensions;
using ProductsCatalog.Models.Domain;
using ProductsCatalog.Models.ReviewAttachment;
using ProductsCatalog.Services.Database;
using ProductsCatalog.Services.Exceptions;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Web;

namespace ProductsCatalog.Services.Domain
{
public class ReviewAttachmentService
{
private static readonly string ReviewAttachmentsDirectory =
AppSettings.Get("ReviewAttachmentsDirectory");

private readonly UploadsManager uploadsManager = new UploadsManager(ReviewAttachmentsDirectory);

public FileStream GetFileStream(ReviewAttachment attachment) =>
this.uploadsManager.GetFileAsStream(
attachment.RelativePath);

public Task<ReviewAttachment[]> GetReviewAttachments(int reviewId) =>
CatalogDatabase.Query<ReviewAttachment>(
"SELECT a.* " +
"FROM V_ReviewAttachment a " +
"WHERE a.ReviewId=@ReviewId",
new { reviewId });

public Task<ReviewAttachment> Get(int attachId) =>
CatalogDatabase.QueryFirstOrDefault<ReviewAttachment>(
"SELECT * " +
"FROM V_ReviewAttachment " +
"WHERE Id=@AttachId",
new { attachId });

public Task<string> GetRelativePath(int attachId) =>
CatalogDatabase.ExecuteScalar<string>(
"SELECT RelativePath " +
"FROM ReviewAttachment " +
"WHERE Id=@AttachId",
new { attachId });

public async Task<bool> Delete(int attachId)
{
var path = await GetRelativePath(attachId);
var result = await CatalogDatabase.Execute(
"DELETE FROM ReviewAttachment " +
"WHERE Id=@AttachId",
new {attachId});
if (result)
this.uploadsManager.DeleteFile(path);
return result;
}

public async Task<int[]> AddAttachments(
ReviewAttachmentInputInsertModel model)
{
ValidateFiles(model.Attachments);

var models = await SaveFiles(model);
var result = new int[models.Length];

using (var conn = await CatalogDatabase.OpenConnection())
using (var tran = conn.BeginTransaction())
{
for (int i = 0; i < models.Length; i++)
{
var id = await CatalogDatabase.ExecuteScalar<int>(
"INSERT INTO ReviewAttachment " +
"(ReviewId, RelativePath, Filename, ContentType, UploadedBy, UploadedAt) " +
"OUTPUT INSERTED.Id " +
"VALUES (@ReviewId, @RelativePath, @Filename, @ContentType, @UploadedBy, @UploadedAt)",
models[i]);
result[i] = id;
}
tran.Commit();
}

return result;
}

public Task<ReviewAttachment[]> SaveFiles(ReviewAttachmentInputInsertModel model) =>
Task.WhenAll(model
.Attachments
.FilesCollection()
.Select(async x => new ReviewAttachment
{
ReviewId = model.ReviewId,
RelativePath = await this.uploadsManager.SaveFile(x.InputStream),
Filename = x.FileName,
ContentType = x.ContentType,
UploadedBy = UserManager.UserId,
UploadedAt = DateTime.Now
}));

public async Task<int> AddAttachmentFromUrl(
ReviewAttachmentFromUrlInputInsertModel model)
{
var file = await ImageDownloader.Download(model.Url);
if (this.uploadsManager.IsFileExists(file.Stream))
throw new GeneralApiException(
$"File {file.FileName} already exists");

var attachModel = new ReviewAttachment
{
ReviewId = model.ReviewId,
RelativePath = await this.uploadsManager.SaveFile(file.Stream),
Filename = file.FileName,
ContentType = file.ContentType,
UploadedBy = UserManager.UserId,
UploadedAt = DateTime.Now
};
return await CatalogDatabase.ExecuteScalar<int>(
"INSERT INTO ReviewAttachment " +
"(ReviewId, RelativePath, Filename, ContentType, UploadedBy, UploadedAt) " +
"OUTPUT INSERTED.Id " +
"VALUES (@ReviewId, @RelativePath, @Filename, @ContentType, @UploadedBy, @UploadedAt)",
attachModel);
}

public void ValidateFiles(HttpFileCollection files)
{
foreach (var file in files.FilesCollection())
{
if (file.ContentLength > 10 * 1024 * 1024)
throw new GeneralApiException("Maximum file size is 10Mb");

if (!ImageDownloader.ImageHeaders.ContainsKey(
file.ContentType))
throw new GeneralApiException(
"Unacceptable content-type");

if (!ImageDownloader.IsValidHeader(
file.ContentType, file.InputStream))
throw new GeneralApiException(
"Invalid file header");

if (this.uploadsManager.IsFileExists(file.InputStream))
throw new GeneralApiException(
$"File {file.FileName} already exists");
}
}
}
}
above is the question
1st option

using ProductsCatalog.Extensions;
using ProductsCatalog.Models.Domain;
using ProductsCatalog.Models.ReviewAttachment;
using ProductsCatalog.Services.Database;
using ProductsCatalog.Services.Exceptions;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Web;

namespace ProductsCatalog.Services.Domain
{
public class ReviewAttachmentService
{
private static readonly string ReviewAttachmentsDirectory =
AppSettings.Get("ReviewAttachmentsDirectory");

private readonly UploadsManager uploadsManager = new UploadsManager(ReviewAttachmentsDirectory);

public int UserId => int.TryParse(
HttpContext.Current.Request.Headers["X-USER-ID"],
out var userId)
? userId
: throw new AccessDeniedException("Missing X-USER-ID header");

public static async Task<bool> ValidateOwner(int attachId, int userId)
{
var ownerId = await CatalogDatabase.ExecuteScalar<int?>(
"SELECT UploadedBy " +
"FROM ReviewAttachment " +
"WHERE Id=@AttachId",
new { attachId });
if (ownerId == null)
throw new RecordNotFoundException($"Attachment {attachId} doesn't exists");
else
return ownerId == userId;
}

public FileStream GetFileStream(ReviewAttachment attachment) =>
this.uploadsManager.GetFileAsStream(
attachment.RelativePath);

public Task<ReviewAttachment[]> GetReviewAttachments(int reviewId) =>
CatalogDatabase.Query<ReviewAttachment>(
"SELECT a.* " +
"FROM V_ReviewAttachment a " +
"WHERE a.ReviewId=@ReviewId",
new { reviewId });

public Task<ReviewAttachment> Get(int attachId) =>
CatalogDatabase.QueryFirstOrDefault<ReviewAttachment>(
"SELECT * " +
"FROM V_ReviewAttachment " +
"WHERE Id=@AttachId",
new { attachId });

public Task<string> GetRelativePath(int attachId) =>
CatalogDatabase.ExecuteScalar<string>(
"SELECT RelativePath " +
"FROM ReviewAttachment " +
"WHERE Id=@AttachId",
new { attachId });

public async Task<bool> Delete(int attachId)
{
if (!await ValidateOwner(attachId, UserId))
throw new AccessDeniedException(
"Cannot delete attachment of another user");

var path = await GetRelativePath(attachId);
var result = await CatalogDatabase.Execute(
"DELETE FROM ReviewAttachment " +
"WHERE Id=@AttachId",
new {attachId});
if (result)
this.uploadsManager.DeleteFile(path);
return result;
}

public async Task<int[]> AddAttachments(
ReviewAttachmentInputInsertModel model)
{
if (!await ProductReviewService.ValidateOwner(model.ReviewId, UserId))
throw new AccessDeniedException(
"Cannot add attachments to a review of another user");
ValidateFiles(model.Attachments);

var models = await SaveFiles(model);
var result = new int[models.Length];

using (var conn = await CatalogDatabase.OpenConnection())
using (var tran = conn.BeginTransaction())
{
for (int i = 0; i < models.Length; i++)
{
var id = await CatalogDatabase.ExecuteScalar<int>(
"INSERT INTO ReviewAttachment " +
"(ReviewId, RelativePath, Filename, ContentType, UploadedBy, UploadedAt) " +
"OUTPUT INSERTED.Id " +
"VALUES (@ReviewId, @RelativePath, @Filename, @ContentType, @UploadedBy, @UploadedAt)",
models[i]);
result[i] = id;
}
tran.Commit();
}

return result;
}

public Task<ReviewAttachment[]> SaveFiles(ReviewAttachmentInputInsertModel model) =>
Task.WhenAll(model
.Attachments
.FilesCollection()
.Select(async x => new ReviewAttachment
{
ReviewId = model.ReviewId,
RelativePath = await this.uploadsManager.SaveFile(x.InputStream),
Filename = x.FileName,
ContentType = x.ContentType,
UploadedBy = UserManager.UserId,
UploadedAt = DateTime.Now
}));

public async Task<int> AddAttachmentFromUrl(
ReviewAttachmentFromUrlInputInsertModel model)
{
if (!await ProductReviewService.ValidateOwner(model.ReviewId, UserId))
throw new AccessDeniedException(
"Cannot add attachments to a review of another user");

var file = await ImageDownloader.Download(model.Url);
if (this.uploadsManager.IsFileExists(file.Stream))
throw new GeneralApiException(
$"File {file.FileName} already exists");

var attachModel = new ReviewAttachment
{
ReviewId = model.ReviewId,
RelativePath = await this.uploadsManager.SaveFile(file.Stream),
Filename = file.FileName,
ContentType = file.ContentType,
UploadedBy = UserManager.UserId,
UploadedAt = DateTime.Now
};
return await CatalogDatabase.ExecuteScalar<int>(
"INSERT INTO ReviewAttachment " +
"(ReviewId, RelativePath, Filename, ContentType, UploadedBy, UploadedAt) " +
"OUTPUT INSERTED.Id " +
"VALUES (@ReviewId, @RelativePath, @Filename, @ContentType, @UploadedBy, @UploadedAt)",
attachModel);
}

public void ValidateFiles(HttpFileCollection files)
{
foreach (var file in files.FilesCollection())
{
if (file.ContentLength > 10 * 1024 * 1024)
throw new GeneralApiException("Maximum file size is 10Mb");

if (!ImageDownloader.ImageHeaders.ContainsKey(
file.ContentType))
throw new GeneralApiException(
"Unacceptable content-type");

if (!ImageDownloader.IsValidHeader(
file.ContentType, file.InputStream))
throw new GeneralApiException(
"Invalid file header");

if (this.uploadsManager.IsFileExists(file.InputStream))
throw new GeneralApiException(
$"File {file.FileName} already exists");
}
}
}
}
2
ion
using ProductsCatalog.Extensions;
using ProductsCatalog.Models.Domain;
using ProductsCatalog.Models.ReviewAttachment;
using ProductsCatalog.Services.Database;
using ProductsCatalog.Services.Exceptions;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Web;

namespace ProductsCatalog.Services.Domain
{
public class ReviewAttachmentService
{
private static readonly string ReviewAttachmentsDirectory =
AppSettings.Get("ReviewAttachmentsDirectory");

private readonly UploadsManager uploadsManager = new UploadsManager(ReviewAttachmentsDirectory);

public FileStream GetFileStream(ReviewAttachment attachment) =>
this.uploadsManager.GetFileAsStream(
attachment.RelativePath);

public Task<ReviewAttachment[]> GetReviewAttachments(int reviewId) =>
CatalogDatabase.Query<ReviewAttachment>(
"SELECT a.* " +
"FROM V_ReviewAttachment a " +
"WHERE a.ReviewId=@ReviewId",
new { reviewId });

public Task<ReviewAttachment> Get(int attachId) =>
CatalogDatabase.QueryFirstOrDefault<ReviewAttachment>(
"SELECT * " +
"FROM V_ReviewAttachment " +
"WHERE Id=@AttachId",
new { attachId });

public Task<string> GetRelativePath(int attachId) =>
CatalogDatabase.ExecuteScalar<string>(
"SELECT RelativePath " +
"FROM ReviewAttachment " +
"WHERE Id=@AttachId",
new { attachId });

public async Task<bool> Delete(int attachId)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
throw new AccessDeniedException(
"Cannot delete attachment of another user");

var path = await GetRelativePath(attachId);
var result = await CatalogDatabase.Execute(
"DELETE FROM ReviewAttachment " +
"WHERE Id=@AttachId",
new {attachId});
if (result)
this.uploadsManager.DeleteFile(path);
return result;
}

public async Task<int[]> AddAttachments(
ReviewAttachmentInputInsertModel model)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
throw new AccessDeniedException(
"Cannot add attachments to a review of another user");

ValidateFiles(model.Attachments);

var models = await SaveFiles(model);
var result = new int[models.Length];

using (var conn = await CatalogDatabase.OpenConnection())
using (var tran = conn.BeginTransaction())
{
for (int i = 0; i < models.Length; i++)
{
var id = await CatalogDatabase.ExecuteScalar<int>(
"INSERT INTO ReviewAttachment " +
"(ReviewId, RelativePath, Filename, ContentType, UploadedBy, UploadedAt) " +
"OUTPUT INSERTED.Id " +
"VALUES (@ReviewId, @RelativePath, @Filename, @ContentType, @UploadedBy, @UploadedAt)",
models[i]);
result[i] = id;
}
tran.Commit();
}

return result;
}

public Task<ReviewAttachment[]> SaveFiles(ReviewAttachmentInputInsertModel model) =>
Task.WhenAll(model
.Attachments
.FilesCollection()
.Select(async x => new ReviewAttachment
{
ReviewId = model.ReviewId,
RelativePath = await this.uploadsManager.SaveFile(x.InputStream),
Filename = x.FileName,
ContentType = x.ContentType,
UploadedBy = UserManager.UserId,
UploadedAt = DateTime.Now
}));

public async Task<int> AddAttachmentFromUrl(
ReviewAttachmentFromUrlInputInsertModel model)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
throw new AccessDeniedException(
"Cannot add attachments to a review of another user");

var file = await ImageDownloader.Download(model.Url);
if (this.uploadsManager.IsFileExists(file.Stream))
throw new GeneralApiException(
$"File {file.FileName} already exists");

var attachModel = new ReviewAttachment
{
ReviewId = model.ReviewId,
RelativePath = await this.uploadsManager.SaveFile(file.Stream),
Filename = file.FileName,
ContentType = file.ContentType,
UploadedBy = UserManager.UserId,
UploadedAt = DateTime.Now
};
return await CatalogDatabase.ExecuteScalar<int>(
"INSERT INTO ReviewAttachment " +
"(ReviewId, RelativePath, Filename, ContentType, UploadedBy, UploadedAt) " +
"OUTPUT INSERTED.Id " +
"VALUES (@ReviewId, @RelativePath, @Filename, @ContentType, @UploadedBy, @UploadedAt)",
attachModel);
}

public void ValidateFiles(HttpFileCollection files)
{
foreach (var file in files.FilesCollection())
{
if (file.ContentLength > 10 * 1024 * 1024)
throw new GeneralApiException("Maximum file size is 10Mb");

if (!ImageDownloader.ImageHeaders.ContainsKey(
file.ContentType))
throw new GeneralApiException(
"Unacceptable content-type");

if (!ImageDownloader.IsValidHeader(
file.ContentType, file.InputStream))
throw new GeneralApiException(
"Invalid file header");

if (this.uploadsManager.IsFileExists(file.InputStream))
throw new GeneralApiException(
$"File {file.FileName} already exists");
}
}
}
3

using ProductsCatalog.Extensions;
using ProductsCatalog.Models.Domain;
using ProductsCatalog.Models.ReviewAttachment;
using ProductsCatalog.Services.Database;
using ProductsCatalog.Services.Exceptions;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Web;

namespace ProductsCatalog.Services.Domain
{
public class ReviewAttachmentService
{
private static readonly string ReviewAttachmentsDirectory =
AppSettings.Get("ReviewAttachmentsDirectory");

private readonly UploadsManager uploadsManager = new UploadsManager(ReviewAttachmentsDirectory);

public FileStream GetFileStream(ReviewAttachment attachment) =>
this.uploadsManager.GetFileAsStream(
attachment.RelativePath);

public Task<ReviewAttachment[]> GetReviewAttachments(int reviewId) =>
CatalogDatabase.Query<ReviewAttachment>(
"SELECT a.* " +
"FROM V_ReviewAttachment a " +
"WHERE a.ReviewId=@ReviewId",
new { reviewId });

public Task<ReviewAttachment> Get(int attachId) =>
CatalogDatabase.QueryFirstOrDefault<ReviewAttachment>(
"SELECT * " +
"FROM V_ReviewAttachment " +
"WHERE Id=@AttachId",
new { attachId });

public Task<string> GetRelativePath(int attachId) =>
CatalogDatabase.ExecuteScalar<string>(
"SELECT RelativePath " +
"FROM ReviewAttachment " +
"WHERE Id=@AttachId",
new { attachId });

public async Task<bool> Delete(ReviewAttachmentInputDeleteModel model)
{
if (!await ProductReviewService.ValidateOwner(model.AttachId, model.UserId))
throw new AccessDeniedException(
"Cannot delete attachment of another user");

var path = await GetRelativePath(model.AttachId);
var result = await CatalogDatabase.Execute(
"DELETE FROM ReviewAttachment " +
"WHERE Id=@AttachId",
model);
if (result)
this.uploadsManager.DeleteFile(path);
return result;
}

public async Task<int[]> AddAttachments(
ReviewAttachmentInputInsertModel model)
{
if (!await ProductReviewService.ValidateOwner(
model.ReviewId,
model.UserId))
throw new AccessDeniedException(
"Cannot add attachments to a review of another user");
ValidateFiles(model.Attachments);

var models = await SaveFiles(model);
var result = new int[models.Length];

using (var conn = await CatalogDatabase.OpenConnection())
using (var tran = conn.BeginTransaction())
{
for (int i = 0; i < models.Length; i++)
{
var id = await CatalogDatabase.ExecuteScalar<int>(
"INSERT INTO ReviewAttachment " +
"(ReviewId, RelativePath, Filename, ContentType, UploadedBy, UploadedAt) " +
"OUTPUT INSERTED.Id " +
"VALUES (@ReviewId, @RelativePath, @Filename, @ContentType, @UploadedBy, @UploadedAt)",
models[i]);
result[i] = id;
}
tran.Commit();
}

return result;
}

public Task<ReviewAttachment[]> SaveFiles(ReviewAttachmentInputInsertModel model) =>
Task.WhenAll(model
.Attachments
.FilesCollection()
.Select(async x => new ReviewAttachment
{
ReviewId = model.ReviewId,
RelativePath = await this.uploadsManager.SaveFile(x.InputStream),
Filename = x.FileName,
ContentType = x.ContentType,
UploadedBy = UserManager.UserId,
UploadedAt = DateTime.Now
}));

public async Task<int> AddAttachmentFromUrl(
ReviewAttachmentFromUrlInputInsertModel model)
{
if (!await ProductReviewService.ValidateOwner(
model.ReviewId,
model.UserId))
throw new AccessDeniedException(
"Cannot add attachments to a review of another user");

var file = await ImageDownloader.Download(model.Url);
if (this.uploadsManager.IsFileExists(file.Stream))
throw new GeneralApiException(
$"File {file.FileName} already exists");

var attachModel = new ReviewAttachment
{
ReviewId = model.ReviewId,
RelativePath = await this.uploadsManager.SaveFile(file.Stream),
Filename = file.FileName,
ContentType = file.ContentType,
UploadedBy = UserManager.UserId,
UploadedAt = DateTime.Now
};
return await CatalogDatabase.ExecuteScalar<int>(
"INSERT INTO ReviewAttachment " +
"(ReviewId, RelativePath, Filename, ContentType, UploadedBy, UploadedAt) " +
"OUTPUT INSERTED.Id " +
"VALUES (@ReviewId, @RelativePath, @Filename, @ContentType, @UploadedBy, @UploadedAt)",
attachModel);
}

public void ValidateFiles(HttpFileCollection files)
{
foreach (var file in files.FilesCollection())
{
if (file.ContentLength > 10 * 1024 * 1024)
throw new GeneralApiException("Maximum file size is 10Mb");

if (!ImageDownloader.ImageHeaders.ContainsKey(
file.ContentType))
throw new GeneralApiException(
"Unacceptable content-type");

if (!ImageDownloader.IsValidHeader(
file.ContentType, file.InputStream))
throw new GeneralApiException(
"Invalid file header");

if (this.uploadsManager.IsFileExists(file.InputStream))
throw new GeneralApiException(
$"File {file.FileName} already exists");
}
}
}
}
4
using ProductsCatalog.Extensions;
using ProductsCatalog.Models.Domain;
using ProductsCatalog.Models.ReviewAttachment;
using ProductsCatalog.Services.Database;
using ProductsCatalog.Services.Exceptions;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Web;

namespace ProductsCatalog.Services.Domain
{
public class ReviewAttachmentService
{
private static readonly string ReviewAttachmentsDirectory =
AppSettings.Get("ReviewAttachmentsDirectory");

private readonly UploadsManager uploadsManager = new UploadsManager(ReviewAttachmentsDirectory);

public static async Task<bool> ValidateOwner(int attachId, int userId)
{
var ownerId = await CatalogDatabase.ExecuteScalar<int?>(
"SELECT UploadedBy " +
"FROM ReviewAttachment " +
"WHERE Id=@AttachId",
new { attachId });
if (ownerId == null)
throw new RecordNotFoundException($"Attachment {attachId} doesn't exists");
else
return ownerId == userId;
}

public FileStream GetFileStream(ReviewAttachment attachment) =>
this.uploadsManager.GetFileAsStream(
attachment.RelativePath);

public Task<ReviewAttachment[]> GetReviewAttachments(int reviewId) =>
CatalogDatabase.Query<ReviewAttachment>(
"SELECT a.* " +
"FROM V_ReviewAttachment a " +
"WHERE a.ReviewId=@ReviewId",
new { reviewId });

public Task<ReviewAttachment> Get(int attachId) =>
CatalogDatabase.QueryFirstOrDefault<ReviewAttachment>(
"SELECT * " +
"FROM V_ReviewAttachment " +
"WHERE Id=@AttachId",
new { attachId });

public Task<string> GetRelativePath(int attachId) =>
CatalogDatabase.ExecuteScalar<string>(
"SELECT RelativePath " +
"FROM ReviewAttachment " +
"WHERE Id=@AttachId",
new { attachId });

public async Task<bool> Delete(int attachId)
{
if (!await ValidateOwner( attachId, UserManager.UserId))
throw new AccessDeniedException(
"Cannot delete attachment of another user");

var path = await GetRelativePath(attachId);
var result = await CatalogDatabase.Execute(
"DELETE FROM ReviewAttachment " +
"WHERE Id=@AttachId",
new {attachId});
if (result)
this.uploadsManager.DeleteFile(path);
return result;
}

public async Task<int[]> AddAttachments(
ReviewAttachmentInputInsertModel model)
{
if (!await ProductReviewService.ValidateOwner(
model.ReviewId,
UserManager.UserId))
throw new AccessDeniedException(
"Cannot add attachments to a review of another user");
ValidateFiles(model.Attachments);

var models = await SaveFiles(model);
var result = new int[models.Length];

using (var conn = await CatalogDatabase.OpenConnection())
using (var tran = conn.BeginTransaction())
{
for (int i = 0; i < models.Length; i++)
{
var id = await CatalogDatabase.ExecuteScalar<int>(
"INSERT INTO ReviewAttachment " +
"(ReviewId, RelativePath, Filename, ContentType, UploadedBy, UploadedAt) " +
"OUTPUT INSERTED.Id " +
"VALUES (@ReviewId, @RelativePath, @Filename, @ContentType, @UploadedBy, @UploadedAt)",
models[i]);
result[i] = id;
}
tran.Commit();
}

return result;
}

public Task<ReviewAttachment[]> SaveFiles(ReviewAttachmentInputInsertModel model) =>
Task.WhenAll(model
.Attachments
.FilesCollection()
.Select(async x => new ReviewAttachment
{
ReviewId = model.ReviewId,
RelativePath = await this.uploadsManager.SaveFile(x.InputStream),
Filename = x.FileName,
ContentType = x.ContentType,
UploadedBy = UserManager.UserId,
UploadedAt = DateTime.Now
}));

public async Task<int> AddAttachmentFromUrl(
ReviewAttachmentFromUrlInputInsertModel model)
{
if (!await ProductReviewService.ValidateOwner(
model.ReviewId,
UserManager.UserId))
throw new AccessDeniedException(
"Cannot add attachments to a review of another user");

var file = await ImageDownloader.Download(model.Url);
if (this.uploadsManager.IsFileExists(file.Stream))
throw new GeneralApiException(
$"File {file.FileName} already exists");

var attachModel = new ReviewAttachment
{
ReviewId = model.ReviewId,
RelativePath = await this.uploadsManager.SaveFile(file.Stream),
Filename = file.FileName,
ContentType = file.ContentType,
UploadedBy = UserManager.UserId,
UploadedAt = DateTime.Now
};
return await CatalogDatabase.ExecuteScalar<int>(
"INSERT INTO ReviewAttachment " +
"(ReviewId, RelativePath, Filename, ContentType, UploadedBy, UploadedAt) " +
"OUTPUT INSERTED.Id " +
"VALUES (@ReviewId, @RelativePath, @Filename, @ContentType, @UploadedBy, @UploadedAt)",
attachModel);
}

public void ValidateFiles(HttpFileCollection files)
{
foreach (var file in files.FilesCollection())
{
if (file.ContentLength > 10 * 1024 * 1024)
throw new GeneralApiException("Maximum file size is 10Mb");

if (!ImageDownloader.ImageHeaders.ContainsKey(
file.ContentType))
throw new GeneralApiException(
"Unacceptable content-type");

if (!ImageDownloader.IsValidHeader(
file.ContentType, file.InputStream))
throw new GeneralApiException(
"Invalid file header");

if (this.uploadsManager.IsFileExists(file.InputStream))
throw new GeneralApiException(
$"File {file.FileName} already exists");
}
}
}
}
     
 
what is notes.io
 

Notes is a web-based application for online taking notes. You can take your notes and share with others people. If you like taking long notes, notes.io is designed for you. To date, over 8,000,000,000+ notes created and continuing...

With notes.io;

  • * You can take a note from anywhere and any device with internet connection.
  • * You can share the notes in social platforms (YouTube, Facebook, Twitter, instagram etc.).
  • * You can quickly share your contents without website, blog and e-mail.
  • * You don't need to create any Account to share a note. As you wish you can use quick, easy and best shortened notes with sms, websites, e-mail, or messaging services (WhatsApp, iMessage, Telegram, Signal).
  • * Notes.io has fabulous infrastructure design for a short link and allows you to share the note as an easy and understandable link.

Fast: Notes.io is built for speed and performance. You can take a notes quickly and browse your archive.

Easy: Notes.io doesn’t require installation. Just write and share note!

Short: Notes.io’s url just 8 character. You’ll get shorten link of your note when you want to share. (Ex: notes.io/q )

Free: Notes.io works for 14 years and has been free since the day it was started.


You immediately create your first note and start sharing with the ones you wish. If you want to contact us, you can use the following communication channels;


Email: [email protected]

Twitter: http://twitter.com/notesio

Instagram: http://instagram.com/notes.io

Facebook: http://facebook.com/notesio



Regards;
Notes.io Team

     
 
Shortened Note Link
 
 
Looding Image
 
     
 
Long File
 
 

For written notes was greater than 18KB Unable to shorten.

To be smaller than 18KB, please organize your notes, or sign in.