Select Git revision
ResourceController.cs

L. Ellenbeck authored and
Petar Hristov
committed
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ResourceController.cs 26.64 KiB
using Coscine.Action;
using Coscine.Action.EventArgs;
using Coscine.ApiCommons;
using Coscine.Configuration;
using Coscine.Database.DataModel;
using Coscine.Database.Models;
using Coscine.Database.ReturnObjects;
using Coscine.Database.Util;
using Coscine.Logging;
using Coscine.ResourceTypes;
using Coscine.ResourceTypes.ResourceTypeConfigs;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Coscine.Api.Resources.Controllers
{
/// <summary>
/// This controller represents the actions which can be taken with a resource object.
/// </summary>
[Authorize]
public class ResourceController : Controller
{
private readonly Authenticator _authenticator;
private readonly ResourceModel _resourceModel;
private readonly IConfiguration _configuration;
private readonly Emitter _emitter;
private readonly ProjectRoleModel _projectRoleModel;
private readonly ProjectResourceModel _projectResourceModel;
private readonly CoscineLogger _coscineLogger;
private readonly ResourceTypeModel _resourceTypeModel;
private readonly VisibilityModel _visibilityModel;
private readonly LicenseModel _licenseModel;
/// <summary>
/// ResourceController constructor specifying a ResourceModel.
/// </summary>
public ResourceController(ILogger<ResourceController> logger)
{
_authenticator = new Authenticator(this, Program.Configuration);
_configuration = Program.Configuration;
_resourceModel = new ResourceModel();
_emitter = new Emitter(_configuration);
_projectRoleModel = new ProjectRoleModel();
_projectResourceModel = new ProjectResourceModel();
_coscineLogger = new CoscineLogger(logger);
_resourceTypeModel = new ResourceTypeModel();
_visibilityModel = new VisibilityModel();
_licenseModel = new LicenseModel();
}
/// <summary>
/// Returns a list of all resources the current user has access to.
/// </summary>
/// <returns>List of Resources</returns>
[Route("[controller]")]
public IActionResult Index()
{
var user = _authenticator.GetUser();
return Json(_resourceModel.GetAllWhere((resource) =>
(from projectResource in resource.ProjectResources
where (from projectRole in projectResource.Project.ProjectRoles
where projectRole.User == user
&& (projectRole.Role.DisplayName == "Owner" || projectRole.Role.DisplayName == "Member")
&& !projectRole.Project.Deleted
select projectRole).Any()
select projectResource).Any()
).Select((resource) => Helpers.CreateResourceReturnObject(resource)));
}
/// <summary>
/// Returns the resource with a specified id.
/// </summary>
/// <param name="id">The resource id.</param>
/// <returns>ResourceObject if OK, 401 if not authorized</returns>
[HttpGet("[controller]/{id}")]
public IActionResult Get(Guid id)
{
var resource = _resourceModel.GetById(id);
var user = _authenticator.GetUser();
// Rights Matrix (https://git.rwth-aachen.de/coscine/docs/private/internal-wiki/-/blob/master/coscine/Definition%20of%20rights%20Matrix.md)
// - Resource: View Resource (RCV, Metadatamanager)
if (_resourceModel.HasAccess(user, resource, UserRoles.Owner, UserRoles.Member, UserRoles.Guest))
{
_resourceModel.SetType(resource);
var returnObject = Helpers.CreateResourceReturnObject(resource);
if (_resourceModel.HasAccess(user, resource, UserRoles.Guest))
{
returnObject.ResourceTypeOption = null;
}
return Json(returnObject);
}
else
{
return Unauthorized("User does not own this resource!");
}
}
/// <summary>
/// Returns whether or not the current user is creator of a specified resource.
/// </summary>
/// <param name="id">The resource id.</param>
/// <returns>JSON object.</returns>
[HttpGet("[controller]/{id}/isCreator")]
public IActionResult IsUserResourceCreator(Guid id)
{
var resource = _resourceModel.GetById(id);
var user = _authenticator.GetUser();
return Json(new JObject
{
["isResourceCreator"] = resource.Creator.Equals(user.Id)
});
}
/// <summary>
/// Updates a resource.
/// </summary>
/// <param name="id">The resource id.</param>
/// <param name="resourceObject">Entry representing the user</param>
/// <returns>JSON object.</returns>
[HttpPost("[controller]/{id}")]
public IActionResult Update(Guid id, [FromBody] ResourceObject resourceObject)
{
var resource = _resourceModel.GetById(id);
var resourceTypeModel = new ResourceTypeModel();
var resourceType = resourceTypeModel.GetById(resource.TypeId);
if (resource.Archived == "0")
{
// TODO: Consider making all those checks automatically as annotations over the Object<ResourceObject>
if (!(string.IsNullOrWhiteSpace(resourceObject.DisplayName) ||
string.IsNullOrWhiteSpace(resourceObject.ResourceName) ||
string.IsNullOrWhiteSpace(resourceObject.Description) ||
resourceObject.Disciplines?.Any() != true ||
resourceObject.Visibility == null))
{
var user = _authenticator.GetUser();
// Rights Matrix (https://git.rwth-aachen.de/coscine/docs/private/internal-wiki/-/blob/master/coscine/Definition%20of%20rights%20Matrix.md)
// - Resource: Change Resource Settings
if (_resourceModel.HasAccess(user, resource, UserRoles.Owner) ||
(_resourceModel.HasAccess(user, resource, UserRoles.Member) && resource.Creator.Equals(user.Id)))
{
LogAnalyticsEditResource(resource, _resourceModel.GetMetadataCompleteness(resourceObject), resourceObject.Disciplines, user);
var result = SetResourceTypeOptions(resource, resourceType, resourceObject);
if (result != null)
{
return result;
}
var resourceTypeDefinition = ResourceTypeFactory.Instance.GetResourceType(resource);
if (resourceTypeDefinition.GetResourceTypeInformation().Result.CanUpdateResource)
{
resourceTypeDefinition.UpdateResource(id.ToString());
}
return Json(_resourceModel.UpdateByObject(resource, resourceObject));
}
else
{
return Unauthorized("The user is not authorized to perform an update on the selected resource!");
}
}
else
{
return BadRequest("Mandatory information of the resource is missing!");
}
}
else
{
return BadRequest("The resource is archived and cannot be modified!");
}
}
/// <summary>
/// Sets a read only status of a given resource.
/// </summary>
/// <param name="id">A GUID as a string that identifies the resource.</param>
/// <param name="status">A boolean value that specifies if the resource is archived.</param>
/// <returns>JSON object.</returns>
[HttpPost("[controller]/{id}/setReadonly")]
public IActionResult SetResourceReadonly(Guid id, bool status)
{
var resource = _resourceModel.GetById(id);
var user = _authenticator.GetUser();
// Rights Matrix (https://git.rwth-aachen.de/coscine/docs/private/internal-wiki/-/blob/master/coscine/Definition%20of%20rights%20Matrix.md)
// - Resource: Change Resource Settings
if (_resourceModel.HasAccess(user, resource, UserRoles.Owner) ||
(_resourceModel.HasAccess(user, resource, UserRoles.Member) && resource.Creator.Equals(user.Id)))
{
var resourceTypeDefinition = ResourceTypeFactory.Instance.GetResourceType(resource);
if (resourceTypeDefinition.GetResourceTypeInformation().Result.CanSetResourceReadonly)
{
resourceTypeDefinition.SetResourceReadonly(id.ToString(), status);
}
// update archived status of the resource
resource.Archived = status ? "1" : "0";
_resourceModel.Update(resource);
var returnObject = Helpers.CreateResourceReturnObject(resource);
if (Request.Query != null && Request.Query["noanalyticslog"] != "true")
{
if (status)
{
LogAnalyticsArchiveResource(resource, user);
}
else
{
LogAnalyticsUnarchiveResource(resource, user);
}
}
return Json(returnObject);
}
else
{
return Unauthorized("The user is not authorized to perform an update on the selected resource!");
}
}
/// <summary>
/// Deletes a resource.
/// </summary>
/// <param name="id">A GUID as a string that identifies the resource.</param>
/// <returns>Deleted ResourceObject if OK, 401 if not</returns>
[HttpDelete("[controller]/{id}")]
public IActionResult Delete(Guid id)
{
var resource = _resourceModel.GetById(id);
var user = _authenticator.GetUser();
// Rights Matrix (https://git.rwth-aachen.de/coscine/docs/private/internal-wiki/-/blob/master/coscine/Definition%20of%20rights%20Matrix.md)
// - Resource: Change Resource Settings
if (_resourceModel.HasAccess(user, resource, UserRoles.Owner) ||
(_resourceModel.HasAccess(user, resource, UserRoles.Member) && resource.Creator.Equals(user.Id)))
{
var resourceObject = Helpers.CreateResourceReturnObject(resource);
LogAnalyticsDeleteResource(resource, _resourceModel.GetMetadataCompleteness(resourceObject), resourceObject.Disciplines, user);
_emitter.EmitResourceDelete(new ResourceEventArgs(_configuration)
{
Resource = resource
});
_resourceModel.DeleteResource(resource);
return Json(resourceObject);
}
else
{
return Unauthorized("The user is not authorized to perform an update on the selected resource!");
}
}
/// <summary>
/// Stores the provided resource object in a specified project.
/// </summary>
/// <param name="projectId">A GUID as a string that identifies the resource.</param>
/// <param name="resourceObject">Entry representing the user</param>
/// <returns>JSON object.</returns>
[HttpPost("[controller]/project/{projectId}")]
public async Task<IActionResult> StoreToProject(Guid projectId, [FromBody] ResourceObject resourceObject)
{
var projectModel = new ProjectModel();
var resourceTypeModel = new ResourceTypeModel();
var resourceType = resourceTypeModel.GetById(resourceObject.Type.Id);
var project = projectModel.GetById(projectId);
var user = _authenticator.GetUser();
if (resourceType.Type == "gitlab")
{
var parseTosAccepted = bool.TryParse(resourceObject.ResourceTypeOption["TosAccepted"]?.ToString(), out var gitlabTosAccepted);
if (!parseTosAccepted)
{
return BadRequest(@"""ResourceTypeOption"" does not contain a valid ""TosAccepted"".");
}
if (!gitlabTosAccepted)
{
return BadRequest("Gitlab Terms of Service not accepted!");
}
}
if (string.IsNullOrWhiteSpace(user.EmailAddress))
{
return Unauthorized("Access denied!");
}
// Rights Matrix (https://git.rwth-aachen.de/coscine/docs/private/internal-wiki/-/blob/master/coscine/Definition%20of%20rights%20Matrix.md)
// - Resource: Create Resource
if (projectModel.HasAccess(user, project, UserRoles.Owner, UserRoles.Member))
{
var orgs = Util.OrganizationsHelper.GetOrganization(user);
if (orgs?.Any() != true)
{
orgs = new List<string> { "*" };
}
if (!Util.ResourceTypeHelper.IsResourceTypeUsable(resourceType, projectId, orgs))
{
return Unauthorized("The user is not authorized to add a new resource of this type to the selected project!");
}
var totalReservedQuota = Helpers.CalculateTotalReservedQuota(resourceType, projectId);
var maximumQuota = Helpers.GetMaximumQuota(projectId, resourceType.Id);
var success = long.TryParse(resourceObject.ResourceTypeOption["Size"]?.ToString(), out long desiredResourceQuota);
var freeQuota = new QuotaDimObject
{
Value = Helpers.ConvertCapacityUnits(maximumQuota, QuotaUnit.GibiBYTE) - Helpers.ConvertCapacityUnits(totalReservedQuota, QuotaUnit.GibiBYTE),
Unit = QuotaUnit.GibiBYTE
};
if (success && desiredResourceQuota > freeQuota.Value)
{
return BadRequest($"You can not create a resource with a quota value of {desiredResourceQuota} GB. You have already reserved {Helpers.ConvertCapacityUnits(totalReservedQuota, QuotaUnit.GibiBYTE)} GB for {resourceObject.Type.DisplayName} resources and have only {freeQuota.Value} GB free.");
}
var resource = _resourceModel.StoreFromObject(resourceObject, user);
var result = SetResourceTypeOptions(resource, resourceType, resourceObject);
if (result != null)
{
return result;
}
var resourceTypeDefinition = ResourceTypeFactory.Instance.GetResourceType(resource);
await resourceTypeDefinition.CreateResource(resource.Id.ToString(), desiredResourceQuota);
projectModel.AddResource(project, resource);
_emitter.EmitResourceCreate(new ResourceEventArgs(_configuration)
{
Resource = resource
});
var resourceReturnObject = Helpers.CreateResourceReturnObject(resource);
LogAnalyticsAddResource(resource, _resourceModel.GetMetadataCompleteness(resourceReturnObject), resourceReturnObject.Disciplines, user);
return Json(resourceReturnObject);
}
else
{
return Unauthorized("The user is not authorized to add a new resource to the selected project!");
}
}
private IActionResult SetResourceTypeOptions(Database.DataModel.Resource resource, Database.DataModel.ResourceType resourceType, ResourceObject resourceObject)
{
GetResourceTypeConfigOptions getConfig = null;
if (resourceType.Type == "rdss3" || resourceType.Type == "rdss3worm")
{
getConfig = new GetRdsResourceTypeConfigOptions { Bucketname = resource.Id.ToString() };
}
else if (resourceType.Type == "gitlab")
{
var accessToken = resourceObject.ResourceTypeOption["AccessToken"]?.ToString();
if (string.IsNullOrWhiteSpace(accessToken))
{
return BadRequest(@"""ResourceTypeOption"" does not contain an valid ""AccessToken"".");
}
var parseResult = int.TryParse(resourceObject.ResourceTypeOption["ProjectId"]?.ToString(), out var gitlabProjectId);
if (!parseResult)
{
return BadRequest(@"""ResourceTypeOption"" does not contain a valid ""ProjectId"".");
}
var repoUrl = resourceObject.ResourceTypeOption["RepoUrl"]?.ToString();
if (string.IsNullOrWhiteSpace(repoUrl))
{
return BadRequest(@"""ResourceTypeOption"" does not contain a valid ""RepoUrl"".");
}
var branch = resourceObject.ResourceTypeOption["Branch"]?.ToString();
if (string.IsNullOrWhiteSpace(branch))
{
return BadRequest(@"""ResourceTypeOption"" does not contain a valid ""Branch"".");
}
var parseTosAccepted = bool.TryParse(resourceObject.ResourceTypeOption["TosAccepted"]?.ToString(), out var gitlabTosAccepted);
if (!parseTosAccepted)
{
return BadRequest(@"""ResourceTypeOption"" does not contain a valid ""TosAccepted"".");
}
getConfig = new GetGitLabResourceTypeConfigOptions { AccessToken = accessToken, ProjectId = gitlabProjectId, RepoUrl = repoUrl, Branch = branch, TosAccepted = gitlabTosAccepted };
}
var config = ResourceTypeFactory.Instance.GetResourceTypeConfig(resourceType.Type, resourceType.SpecificType, getConfig);
ResourceTypeFactory.Instance.SaveResourceTypeToDatabase(resourceType.Type, config, resource);
return null;
}
private async Task LogAnalyticsEditResource(Database.DataModel.Resource resource, string metadataCompleteness, IEnumerable<DisciplineObject> disciplines, User user)
{
await Task.Run(() =>
{
var resourceTypeDisplayName = _resourceTypeModel.GetById(resource.TypeId).DisplayName;
var projectId = _projectResourceModel.GetProjectForResource(resource.Id).Value;
var reserved = GetReservedResourceQuota(resource);
var maximum = GetMaximumResourceQuota(resource, projectId);
var analyticsLogObject = new AnalyticsLogObject
{
Operation = "Edit Resource",
Type = "Action",
RoleId = _projectRoleModel.GetGetUserRoleForProject(projectId, user.Id).ToString(),
ProjectId = projectId.ToString(),
QuotaSize = new List<string> { $"{resourceTypeDisplayName}: {reserved}/{maximum}" },
ResourceId = resource.Id.ToString(),
ApplicationsProfile = resource.ApplicationProfile,
MetadataCompleteness = metadataCompleteness,
License = resource.LicenseId.HasValue ? _licenseModel.GetById(resource.LicenseId.Value)?.DisplayName : null,
Disciplines = disciplines.Select(x => x.DisplayNameEn).ToList(),
Visibility = resource.VisibilityId.HasValue ? _visibilityModel.GetById(resource.VisibilityId.Value)?.DisplayName : null,
};
_coscineLogger.AnalyticsLog(analyticsLogObject);
});
}
private async Task LogAnalyticsAddResource(Database.DataModel.Resource resource, string metadataCompleteness, IEnumerable<DisciplineObject> disciplines, User user)
{
await Task.Run(() =>
{
var resourceTypeDisplayName = _resourceTypeModel.GetById(resource.TypeId).DisplayName;
var projectId = _projectResourceModel.GetProjectForResource(resource.Id).Value;
var reserved = GetReservedResourceQuota(resource);
var maximum = GetMaximumResourceQuota(resource, projectId);
var analyticsLogObject = new AnalyticsLogObject
{
Operation = "Add Resource",
Type = "Action",
RoleId = _projectRoleModel.GetGetUserRoleForProject(projectId, user.Id).ToString(),
ProjectId = projectId.ToString(),
QuotaSize = new List<string> { $"{resourceTypeDisplayName}: {reserved}/{maximum}" },
ResourceId = resource.Id.ToString(),
ApplicationsProfile = resource.ApplicationProfile,
MetadataCompleteness = metadataCompleteness,
License = resource.LicenseId.HasValue ? _licenseModel.GetById(resource.LicenseId.Value)?.DisplayName : null,
Disciplines = disciplines.Select(x => x.DisplayNameEn).ToList(),
Visibility = resource.VisibilityId.HasValue ? _visibilityModel.GetById(resource.VisibilityId.Value)?.DisplayName : null,
};
_coscineLogger.AnalyticsLog(analyticsLogObject);
});
}
private async Task LogAnalyticsDeleteResource(Database.DataModel.Resource resource, string metadataCompleteness, IEnumerable<DisciplineObject> disciplines, User user)
{
await Task.Run(() =>
{
var resourceTypeDisplayName = _resourceTypeModel.GetById(resource.TypeId).DisplayName;
var projectId = _projectResourceModel.GetProjectForResource(resource.Id).Value;
var reserved = GetReservedResourceQuota(resource);
var maximum = GetMaximumResourceQuota(resource, projectId);
var analyticsLogObject = new AnalyticsLogObject
{
Operation = "Delete Resource",
Type = "Action",
RoleId = _projectRoleModel.GetGetUserRoleForProject(projectId, user.Id).ToString(),
ProjectId = projectId.ToString(),
QuotaSize = new List<string> { $"{resourceTypeDisplayName}: {reserved}/{maximum}" },
ResourceId = resource.Id.ToString(),
ApplicationsProfile = resource.ApplicationProfile,
MetadataCompleteness = metadataCompleteness,
License = resource.LicenseId.HasValue ? _licenseModel.GetById(resource.LicenseId.Value)?.DisplayName : null,
Disciplines = disciplines.Select(x => x.DisplayNameEn).ToList(),
Visibility = resource.VisibilityId.HasValue ? _visibilityModel.GetById(resource.VisibilityId.Value)?.DisplayName : null,
};
_coscineLogger.AnalyticsLog(analyticsLogObject);
});
}
private async Task LogAnalyticsArchiveResource(Database.DataModel.Resource resource, User user)
{
await Task.Run(() =>
{
var projectId = _projectResourceModel.GetProjectForResource(resource.Id).Value;
var analyticsLogObject = new AnalyticsLogObject
{
Operation = "Archive Resource",
Type = "Action",
RoleId = _projectRoleModel.GetGetUserRoleForProject(projectId, user.Id).ToString(),
ProjectId = projectId.ToString(),
ResourceId = resource.Id.ToString(),
ApplicationsProfile = resource.ApplicationProfile,
License = resource.LicenseId.HasValue ? _licenseModel.GetById(resource.LicenseId.Value)?.DisplayName : null
};
_coscineLogger.AnalyticsLog(analyticsLogObject);
});
}
private async Task LogAnalyticsUnarchiveResource(Database.DataModel.Resource resource, User user)
{
await Task.Run(() =>
{
var projectId = _projectResourceModel.GetProjectForResource(resource.Id).Value;
var analyticsLogObject = new AnalyticsLogObject
{
Operation = "Unarchive Resource",
Type = "Action",
RoleId = _projectRoleModel.GetGetUserRoleForProject(projectId, user.Id).ToString(),
ProjectId = projectId.ToString(),
ResourceId = resource.Id.ToString(),
ApplicationsProfile = resource.ApplicationProfile,
License = resource.LicenseId.HasValue ? _licenseModel.GetById(resource.LicenseId.Value)?.DisplayName : null
};
_coscineLogger.AnalyticsLog(analyticsLogObject);
});
}
private static int GetReservedResourceQuota(Database.DataModel.Resource resource)
{
var resourceTypeDefinition = ResourceTypeFactory.Instance.GetResourceType(resource);
if (resourceTypeDefinition.GetResourceTypeInformation().Result.IsQuotaAvailable)
{
try
{
return (int)resourceTypeDefinition.GetResourceQuotaAvailable(resource.Id.ToString()).Result;
}
catch (Exception)
{
// Error communicating with the resource
}
}
return 0;
}
private static int GetMaximumResourceQuota(Database.DataModel.Resource resource, Guid parentProjectId)
{
var projectQuotaModel = new ProjectQuotaModel();
var projectQuota =
projectQuotaModel.GetWhere((x) =>
x.ProjectId == parentProjectId &&
x.ResourceTypeId == resource.TypeId);
var reservedForCurrent = GetReservedResourceQuota(resource);
var resourceTypeModel = new ResourceTypeModel();
// Get the resource type
var resourceType = resourceTypeModel.GetById(resource.TypeId);
if (resourceType == null)
{
throw new ArgumentNullException($"Could not find resourceType with id: {resource.TypeId}");
}
var totalReserved = Helpers.CalculateTotalReservedQuota(resourceType, parentProjectId);
return (int)(projectQuota.Quota - (totalReserved.Value - reservedForCurrent));
}
}
}