Skip to content
Snippets Groups Projects
Select Git revision
  • main
  • ws22_23
2 results

CODEOWNERS

Blame
  • Forked from ACS / Public / Teaching materials / Grundgebiete der Informatik 3 / Examples
    Source project has a limited visibility.
    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;
            }
        }
    }