Skip to content
Snippets Groups Projects
Commit 2173544c authored by Janis Daniel Dähne's avatar Janis Daniel Dähne
Browse files

- ensured that only the right users can download the right assets

- formatting
parent 34932efb
No related branches found
No related tags found
No related merge requests found
......@@ -15,14 +15,26 @@ using Microsoft.Extensions.Primitives;
namespace ClientServer.Controllers.Core.Exercises
{
[Route(Constants.ApiPrefix + "assets")]
public class TestAssetController : ControllerWithDb
public class DownloadAssetController : ControllerWithDb
{
public TestAssetController(SyndromDbContext context) : base(context)
/*
* no participation --> not allowed to download asset
* if we set this the browser will not download the json response as a file?? (TODO check that)
* Response.StatusCode = 406;
*
*
* we don't need methods to download exercise description or custom project description assets
* because the frontend already loads the content and thus can locally "download" the file (via blobs)
*/
public DownloadAssetController(SyndromDbContext context) : base(context)
{
}
/// <summary>
/// gets (downloads) the test asset
/// gets (downloads) the normal/submit test asset
/// </summary>
/// <param name="generatedCode">the release code</param>
/// <param name="testId">the test id</param>
......@@ -37,17 +49,27 @@ namespace ClientServer.Controllers.Core.Exercises
int userId = GetUserId();
int exerciseId;
if (asTutor)
{
//check if the user is really a tutor for this exercise (group)
var targetUserGroupId =
var exerciseDataTuple =
await _context.ExerciseReleases.Where(p => p.GeneratedCode == generatedCode)
.Select(p => p.Exercise.UserGroupId)
.Select(p => new {p.Exercise.UserGroupId, p.ExerciseId})
.FirstOrDefaultAsync();
if (!await base.HasGroupPermission(targetUserGroupId,
if (exerciseDataTuple == null)
{
Response.StatusCode = 406;
await
Response.WriteAsync(
Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "participation not found")));
return;
}
if (!await base.HasGroupPermission(exerciseDataTuple.UserGroupId,
permission => permission != null && permission.CanAssessExercises))
{
Response.StatusCode = 406;
......@@ -56,6 +78,8 @@ namespace ClientServer.Controllers.Core.Exercises
Jc.Serialize(new BasicResponse(ResponseCode.NoPermission, "no permission")));
return;
}
exerciseId = exerciseDataTuple.ExerciseId;
}
else
{
......@@ -76,6 +100,15 @@ namespace ClientServer.Controllers.Core.Exercises
return;
}
if (participation.ExerciseRelease == null)
{
Response.StatusCode = 406;
await
Response.WriteAsync(
Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "exercise release not found")));
return;
}
if (isSubmitTest)
{
//submit tests are only displayed when the release is finished...
......@@ -93,14 +126,17 @@ namespace ClientServer.Controllers.Core.Exercises
return;
}
}
exerciseId = participation.ExerciseRelease.ExerciseId;
}
var asset = await this.GetTestNormalOrSubmitTestAsset(testId, assetId, isSubmitTest);
var asset = await this._GetTestNormalOrSubmitTestAsset(testId, assetId, exerciseId, isSubmitTest);
if (asset == null)
{
//already handled
return;
}
else if (asset.Content == null)
{
......@@ -129,24 +165,28 @@ namespace ClientServer.Controllers.Core.Exercises
/// </summary>
/// <param name="testId">the test id</param>
/// <param name="assetId">the asset id</param>
/// <param name="exerciseId"></param>
/// <param name="isSubmitTest">true: submit test, false: not</param>
/// <returns>the asset or null if not found or error</returns>
private async Task<TempTestAsset> GetTestNormalOrSubmitTestAsset(int testId, int assetId, bool isSubmitTest)
private async Task<TempTestAsset> _GetTestNormalOrSubmitTestAsset(int testId, int assetId, int exerciseId,
bool isSubmitTest)
{
//ensure that this is the right asset (check testId too)
//calling method ensured that the user has access to the test
var connection = await _context.TestWithFileAsAssetReferences
.Include(p => p.FileReferenceTestAsset)
.FirstOrDefaultAsync(p => p.TestId == testId && p.FileReferenceTestAssetId == assetId);
.FirstOrDefaultAsync(p => p.TestId == testId &&
p.Test.ExerciseId == exerciseId &&
p.FileReferenceTestAssetId == assetId);
if (connection== null)
if (connection == null)
{
await
Response.WriteAsync(
Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "asset reference connection not found")));
return null;
}
if (connection.FileReferenceTestAsset == null)
{
await
......@@ -154,12 +194,12 @@ namespace ClientServer.Controllers.Core.Exercises
Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "asset reference not found")));
return null;
}
var testAssetsBasePath = Files.GetUploadFilePath(UploadDirType.TestAssets);
var testFilesDict = await Files.ReadUploadedFilesAsArray(testAssetsBasePath, connection.FileReferenceTestAsset.Id);
var testFilesDict =
await Files.ReadUploadedFilesAsArray(testAssetsBasePath, connection.FileReferenceTestAsset.Id);
var fileName = "asset";
if (Files.ValidFileName(connection.FileReferenceTestAsset.OriginalName))
......@@ -170,7 +210,8 @@ namespace ClientServer.Controllers.Core.Exercises
{
//just try to get a file extension...
var lastIndex = connection.FileReferenceTestAsset.OriginalName.LastIndexOf(".", StringComparison.Ordinal);
var lastIndex =
connection.FileReferenceTestAsset.OriginalName.LastIndexOf(".", StringComparison.Ordinal);
if (lastIndex == -1)
{
//no file extension...
......@@ -224,30 +265,33 @@ namespace ClientServer.Controllers.Core.Exercises
var connection = await _context.CustomTestWithFileAsAssetReferences
.Include(p => p.FileReferenceUserFileAsset)
.FirstOrDefaultAsync(p =>
p.CustomTestId == customTestId && p.CustomTest.UserId == userId &&
p.CustomTestId == customTestId &&
p.CustomTest.ExerciseReleaseId == participation.ExerciseReleaseId &&
p.CustomTest.UserId == userId &&
p.FileReferenceUserFileAssetId == assetId)
;
if (connection == null)
{
Response.StatusCode = 406;
await
Response.WriteAsync(Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "asset connection not found")));
Response.WriteAsync(
Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "asset connection not found")));
return;
}
var testAsset = connection.FileReferenceUserFileAsset;
if (testAsset == null)
{
Response.StatusCode = 406;
await
Response.WriteAsync(Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "asset reference not found")));
Response.WriteAsync(
Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "asset reference not found")));
return;
}
var fileName = "asset";
if (Files.ValidFileName(testAsset.OriginalName))
......@@ -271,8 +315,8 @@ namespace ClientServer.Controllers.Core.Exercises
}
}
var filesIdsToRead = new int[] { connection.FileReferenceUserFileAssetId};
var filesIdsToRead = new int[] {connection.FileReferenceUserFileAssetId};
var basePath = Files.GetUploadFilePath(UploadDirType.UserAssets);
var filesDict = await Files.ReadUploadedFilesAsArray(basePath, filesIdsToRead);
......@@ -282,22 +326,22 @@ namespace ClientServer.Controllers.Core.Exercises
if (content == null)
{
await
Response.WriteAsync(Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "asset content not found")));
Response.WriteAsync(
Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "asset content not found")));
return;
}
//see https://stackoverflow.com/questions/20508788/do-i-need-content-type-application-octet-stream-for-file-download
Response.ContentType = "application/octet-stream"; //overwrite global json response
Response.Headers.Add(new KeyValuePair<string, StringValues>("Content-Disposition",
"attachment; filename=\"" + fileName + "\""));
await Response.Body.WriteAsync(content, 0, content.Length);
await Response.Body.FlushAsync();
}
/// <summary>
/// gets (downloads) the custom project custom test asset
/// </summary>
......@@ -311,7 +355,7 @@ namespace ClientServer.Controllers.Core.Exercises
if (!await base.IsLoggedIn(null, false, false)) return;
int userId = GetUserId();
var customProject = await _context.CustomProjects
.FirstOrDefaultAsync(p => p.UserId == userId && //just ensure the project is from the current user
p.Id == customProjectId)
......@@ -324,9 +368,8 @@ namespace ClientServer.Controllers.Core.Exercises
Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "custom project not found")));
return;
}
var oldTest = await _context.CustomProjectTests
.Where(p =>
p.Id == customTestId && p.CustomProjectId == customProjectId)
......@@ -340,35 +383,37 @@ namespace ClientServer.Controllers.Core.Exercises
Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "test not found")));
return;
}
var connection = await _context.CustomProjectTestWithFileAsAssetReferences
var connection = await _context.CustomProjectTestWithFileAsAssetReferences
.Include(p => p.FileReferenceUserFileAsset)
.FirstOrDefaultAsync(p =>
p.CustomProjectTestId == customTestId && p.CustomProjectTest.CustomProjectId == customProject.Id &&
p.CustomProjectTestId == customTestId &&
p.CustomProjectTest.CustomProjectId == customProject.Id &&
p.FileReferenceUserFileAssetId == assetId)
;
if (connection == null)
{
Response.StatusCode = 406;
await
Response.WriteAsync(Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "asset connection not found")));
Response.WriteAsync(
Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "asset connection not found")));
return;
}
var testAsset = connection.FileReferenceUserFileAsset;
if (testAsset == null)
{
Response.StatusCode = 406;
await
Response.WriteAsync(Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "asset reference not found")));
Response.WriteAsync(
Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "asset reference not found")));
return;
}
var fileName = "asset";
if (Files.ValidFileName(testAsset.OriginalName))
......@@ -392,8 +437,8 @@ namespace ClientServer.Controllers.Core.Exercises
}
}
var filesIdsToRead = new int[] { connection.FileReferenceUserFileAssetId};
var filesIdsToRead = new int[] {connection.FileReferenceUserFileAssetId};
var basePath = Files.GetUploadFilePath(UploadDirType.UserAssets);
var filesDict = await Files.ReadUploadedFilesAsArray(basePath, filesIdsToRead);
......@@ -403,20 +448,19 @@ namespace ClientServer.Controllers.Core.Exercises
if (content == null)
{
await
Response.WriteAsync(Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "asset content not found")));
Response.WriteAsync(
Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "asset content not found")));
return;
}
//see https://stackoverflow.com/questions/20508788/do-i-need-content-type-application-octet-stream-for-file-download
Response.ContentType = "application/octet-stream"; //overwrite global json response
Response.Headers.Add(new KeyValuePair<string, StringValues>("Content-Disposition",
"attachment; filename=\"" + fileName + "\""));
await Response.Body.WriteAsync(content, 0, content.Length);
await Response.Body.FlushAsync();
}
/// <summary>
......@@ -426,14 +470,15 @@ namespace ClientServer.Controllers.Core.Exercises
/// <param name="testId">the test id</param>
/// <param name="assetId">the asset reference id</param>
[HttpGet("editor/{exerciseId}/{testId}/{assetId}")]
public async Task GetTestAssetWithoutReleaseCode(int exerciseId, int testId, int assetId)
public async Task GetExerciseEditorTestAssetWithoutReleaseCode(int exerciseId, int testId, int assetId)
{
if (!await base.IsLoggedIn(null, false, false)) return;
int userId = GetUserId();
int managingGroupId = await _context.Exercises
.Where(p => p.Id == exerciseId).Select(p => p.UserGroupId)
.Where(p => p.Id == exerciseId)
.Select(p => p.UserGroupId)
.FirstOrDefaultAsync();
if (managingGroupId == default(int))
......@@ -447,6 +492,7 @@ namespace ClientServer.Controllers.Core.Exercises
//the user group that WILL manages the exercise (after the change)
var targetUserGroup = await _context.UserGroups.FirstOrDefaultAsync(p => p.Id == managingGroupId);
if (targetUserGroup == null)
{
Response.StatusCode = 406;
......@@ -475,17 +521,19 @@ namespace ClientServer.Controllers.Core.Exercises
//ensure that this is the right asset (check testId too)
var connection = await _context.TestWithFileAsAssetReferences
.Include(p => p.FileReferenceTestAsset)
.FirstOrDefaultAsync(p => p.TestId == testId && p.FileReferenceTestAssetId == assetId);
.FirstOrDefaultAsync(p => p.TestId == testId &&
p.Test.ExerciseId == exerciseId &&
p.FileReferenceTestAssetId == assetId);
if (connection== null)
if (connection == null)
{
await
Response.WriteAsync(
Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "asset reference connection not found")));
return;
}
if (connection.FileReferenceTestAsset == null)
{
await
......@@ -493,10 +541,11 @@ namespace ClientServer.Controllers.Core.Exercises
Jc.Serialize(new BasicResponse(ResponseCode.NotFound, "asset reference not found")));
return;
}
var testAssetsBasePath = Files.GetUploadFilePath(UploadDirType.TestAssets);
var testFilesDict = await Files.ReadUploadedFilesAsArray(testAssetsBasePath, connection.FileReferenceTestAsset.Id);
var testFilesDict =
await Files.ReadUploadedFilesAsArray(testAssetsBasePath, connection.FileReferenceTestAsset.Id);
var fileName = "asset";
......@@ -508,7 +557,8 @@ namespace ClientServer.Controllers.Core.Exercises
{
//just try to get a file extension...
var lastIndex = connection.FileReferenceTestAsset.OriginalName.LastIndexOf(".", StringComparison.Ordinal);
var lastIndex =
connection.FileReferenceTestAsset.OriginalName.LastIndexOf(".", StringComparison.Ordinal);
if (lastIndex == -1)
{
//no file extension...
......@@ -539,14 +589,17 @@ namespace ClientServer.Controllers.Core.Exercises
internal class TempTestAsset
{
public int Id { get; set; }
/// <summary>
/// the name of the file/asset
/// </summary>
public string DisplayName { get; set; }
/// <summary>
/// the content of the asset (binary)
/// </summary>
public byte[] Content { get; set; }
/// <summary>
/// the mime type (maybe we need to know what content is stored)
/// </summary>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment