Select Git revision
ResourceController.cs
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ResourceController.cs 23.72 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 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;
private readonly float _oneGb = 1024 * 1024 * 1024;
/// <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>
/// This 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")
select projectRole).Any()
select projectResource).Any()
).Select((resource) => CreateReturnObject(resource)));
}
/// <summary>
/// This returns the resource for the specified id.
/// </summary>
/// <param name="id">A GUID as a string that identifies the resource.</param>
/// <returns>ResourceObject if OK, 401 if not authorized</returns>
[HttpGet("[controller]/{id}")]
public IActionResult Get(string id)
{
var resource = _resourceModel.GetById(Guid.Parse(id));
var user = _authenticator.GetUser();
if (_resourceModel.HasAccess(user, resource, UserRoles.Owner, UserRoles.Member))
{
_resourceModel.SetType(resource);
var returnObject = CreateReturnObject(resource);
return Json(returnObject);
}
else
{
return Unauthorized("User does not own resource!");
}
}
/// <summary>
/// This returns if the current user is the creator of the specified resource.
/// </summary>
/// <param name="id">A GUID as a string that identifies the resource.</param>
/// <returns>JSON object.</returns>
[HttpGet("[controller]/{id}/isCreator")]
public IActionResult IsUserResourceCreator(string id)
{
Resource resource = _resourceModel.GetById(Guid.Parse(id));
var user = _authenticator.GetUser();
return Json(new JObject
{
["isResourceCreator"] = resource.Creator.Equals(user.Id)
});
}
/// <summary>
/// This returns if the current user is the creator of the specified resource.
/// </summary>
/// <param name="id">A GUID as a string that identifies the resource.</param>
/// <param name="resourceObject">Entry representing the user</param>
/// <returns>JSON object.</returns>
[HttpPost("[controller]/{id}")]
public IActionResult Update(string id, [FromBody] ResourceObject resourceObject)
{
var resource = _resourceModel.GetById(Guid.Parse(id));
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 == null || !resourceObject.Disciplines.Any()) ||
resourceObject.Visibility == null))
{
var user = _authenticator.GetUser();
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 resourceTypeDefinition = ResourceTypeFactory.Instance.GetResourceType(resource);
if (resourceTypeDefinition.GetResourceTypeInformation().Result.CanUpdateResource)
{
resourceTypeDefinition.UpdateResource(id);
}
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>
/// This returns the read only status of the 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(string id, bool status)
{
var resource = _resourceModel.GetById(Guid.Parse(id));
var user = _authenticator.GetUser();
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, status);
}
// update archived status of the resource
resource.Archived = status ? "1" : "0";
_resourceModel.Update(resource);
var returnObject = CreateReturnObject(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>
/// This deletes the specified 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(string id)
{
var resource = _resourceModel.GetById(Guid.Parse(id));
var user = _authenticator.GetUser();
if (_resourceModel.HasAccess(user, resource, UserRoles.Owner) ||
(_resourceModel.HasAccess(user, resource, UserRoles.Member) && resource.Creator.Equals(user.Id)))
{
var resourceObject = CreateReturnObject(resource);
LogAnalyticsDeleteResource(resource, _resourceModel.GetMetadataCompleteness(resourceObject), resourceObject.Disciplines, user);
var resourceTypeDefinition = ResourceTypeFactory.Instance.GetResourceType(resource);
if (resourceTypeDefinition.GetResourceTypeInformation().Result.CanDeleteResource)
{
resourceTypeDefinition.DeleteResource(id);
}
_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>
/// This stores the provided resource object for the 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(string projectId, [FromBody] ResourceObject resourceObject)
{
var projectModel = new ProjectModel();
var resourceModel = new ResourceModel();
var resourceTypeModel = new ResourceTypeModel();
var resourceType = resourceTypeModel.GetById(resourceObject.Type.Id);
var project = projectModel.GetById(Guid.Parse(projectId));
var user = _authenticator.GetUser();
if (projectModel.HasAccess(user, project, UserRoles.Owner, UserRoles.Member))
{
if (!resourceType.Enabled.HasValue || !resourceType.Enabled.Value)
{
return Unauthorized("The user is not authorized to add a new resource of this type to the selected project!");
}
// get all resources (of type resourceObject.Type)
// TODO can i get the quota of all resources?
if (resourceType.Type == "rds")
{
var resources = resourceModel.GetAllWhere(x =>
(from projectResource in x.ProjectResources
where projectResource.ProjectId == project.Id
&& projectResource.Resource.Type.Id == resourceObject.Type.Id
select projectResource).Any());
var projectQuotaModel = new ProjectQuotaModel();
var allowedSize = projectQuotaModel.GetAllWhere(x =>
x.ProjectId == project.Id
&& x.ResourceTypeId == resourceObject.Type.Id).First().Quota;
var rdsResourceTypeModel = new RDSResourceTypeModel();
var allocatedSize = resources.Sum(x => rdsResourceTypeModel.GetById(x.ResourceTypeOptionId.Value)?.Size ?? 0);
if (allocatedSize + resourceObject.ResourceTypeOption.ToObject<RDSResourceTypeObject>().Size > allowedSize)
{
return BadRequest($"You cannot create a resource with {resourceObject.ResourceTypeOption.ToObject<RDSResourceTypeObject>().Size} GB of memory. You have already allocated {allocatedSize} GB for your {resourceObject.Type.DisplayName} resources. Maximum are {allowedSize} GB.");
}
}
if (resourceType.Type == "rdss3")
{
var resources = resourceModel.GetAllWhere(x =>
(from projectResource in x.ProjectResources
where projectResource.ProjectId == project.Id
&& projectResource.Resource.Type.Id == resourceObject.Type.Id
select projectResource).Any());
var projectQuotaModel = new ProjectQuotaModel();
var allowedSize = projectQuotaModel.GetAllWhere(x =>
x.ProjectId == project.Id
&& x.ResourceTypeId == resourceObject.Type.Id).First().Quota;
var rdsS3ResourceTypeModel = new RdsS3ResourceTypeModel();
var allocatedSize = resources.Sum(x => rdsS3ResourceTypeModel.GetById(x.ResourceTypeOptionId.Value)?.Size ?? 0);
if (allocatedSize + resourceObject.ResourceTypeOption.ToObject<RdsS3ResourceTypeObject>().Size > allowedSize)
{
return BadRequest($"You cannot create a resource with {resourceObject.ResourceTypeOption.ToObject<RDSResourceTypeObject>().Size} GB of memory. You have already allocated {allocatedSize} GB for your {resourceObject.Type.DisplayName} resources. Maximum are {allowedSize} GB.");
}
}
var success = long.TryParse(resourceObject.ResourceTypeOption["Size"]?.ToString(), out long size);
resourceObject.Creator = user.Id;
var resource = _resourceModel.StoreFromObject(resourceObject);
var config = ResourceTypeFactory.Instance.GetResourceTypeConfig(resourceType.Type, resourceType.SpecificType, new GetResourceTypeConfigOptions { Bucketname = resource.Id.ToString() });
ResourceTypeFactory.Instance.SaveResourceTypeToDatabase(resourceType.Type, config, resource, (int)(success ? size : 0));
var resourceTypeDefinition = ResourceTypeFactory.Instance.GetResourceType(resource);
await resourceTypeDefinition.CreateResource(resource.Id.ToString(), success ? size : null);
projectModel.AddResource(project, resource);
_emitter.EmitResourceCreate(new ResourceEventArgs(_configuration)
{
Resource = resource
});
var resourceReturnObject = CreateReturnObject(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!");
}
}
/// <summary>
/// Get return Object
/// </summary>
/// <param name="resource">Given resource</param>
/// <returns>ResourceObject</returns>
private ResourceObject CreateReturnObject(Resource resource)
{
var returnObject = _resourceModel.CreateReturnObjectFromDatabaseObject(resource);
if (returnObject.ResourceTypeOption.ContainsKey("Size"))
{
var resourceTypeDefinition = ResourceTypeFactory.Instance.GetResourceType(resource);
int allocatedQuota = (int)resourceTypeDefinition.GetResourceQuotaAvailable(resource.Id.ToString()).Result;
if (allocatedQuota != 0)
{
returnObject.ResourceTypeOption["Size"] = allocatedQuota;
}
}
return returnObject;
}
private void LogAnalyticsEditResource(Resource resource, string metadataCompleteness, IEnumerable<DisciplineObject> disciplines, User user)
{
var resourceTypeDisplayName = _resourceTypeModel.GetById(resource.TypeId).DisplayName;
var used = GetUsedResourceQuota(resource);
var allocated = GetAllocatedResourceQuota(resource);
var projectId = _projectResourceModel.GetProjectForResource(resource.Id).Value;
var analyticsLogObject = new AnalyticsLogObject
{
Operation = "Edit Resource",
Type = "Action",
RoleId = _projectRoleModel.GetGetUserRoleForProject(projectId, user.Id).ToString(),
ProjectId = projectId.ToString(),
QuotaSize = new List<string> { $"{resourceTypeDisplayName}: {used}/{allocated}" },
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 void LogAnalyticsAddResource(Resource resource, string metadataCompleteness, IEnumerable<DisciplineObject> disciplines, User user)
{
var resourceTypeDisplayName = _resourceTypeModel.GetById(resource.TypeId).DisplayName;
var used = GetUsedResourceQuota(resource);
var allocated = GetAllocatedResourceQuota(resource);
var projectId = _projectResourceModel.GetProjectForResource(resource.Id).Value;
var analyticsLogObject = new AnalyticsLogObject
{
Operation = "Add Resource",
Type = "Action",
RoleId = _projectRoleModel.GetGetUserRoleForProject(projectId, user.Id).ToString(),
ProjectId = projectId.ToString(),
QuotaSize = new List<string> { $"{resourceTypeDisplayName}: {used}/{allocated}" },
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 void LogAnalyticsDeleteResource(Resource resource, string metadataCompleteness, IEnumerable<DisciplineObject> disciplines, User user)
{
var resourceTypeDisplayName = _resourceTypeModel.GetById(resource.TypeId).DisplayName;
var used = GetUsedResourceQuota(resource);
var allocated = GetAllocatedResourceQuota(resource);
var projectId = _projectResourceModel.GetProjectForResource(resource.Id).Value;
var analyticsLogObject = new AnalyticsLogObject
{
Operation = "Delete Resource",
Type = "Action",
RoleId = _projectRoleModel.GetGetUserRoleForProject(projectId, user.Id).ToString(),
ProjectId = projectId.ToString(),
QuotaSize = new List<string> { $"{resourceTypeDisplayName}: {used}/{allocated}" },
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 void LogAnalyticsArchiveResource(Resource resource, User user)
{
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 void LogAnalyticsUnarchiveResource(Resource resource, User user)
{
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 int GetUsedResourceQuota(Resource resource)
{
var resourceTypeDefinition = ResourceTypeFactory.Instance.GetResourceType(resource);
if (resourceTypeDefinition.GetResourceTypeInformation().Result.IsQuotaAvailable)
{
return (int)Math.Ceiling(resourceTypeDefinition.GetResourceQuotaUsed(resource.Id.ToString()).Result / _oneGb);
}
return 0;
}
private int GetAllocatedResourceQuota(Resource resource)
{
var resourceTypeDefinition = ResourceTypeFactory.Instance.GetResourceType(resource);
if (resourceTypeDefinition.GetResourceTypeInformation().Result.IsQuotaAvailable)
{
return (int)resourceTypeDefinition.GetResourceQuotaAvailable(resource.Id.ToString()).Result;
}
return 0;
}
}
}