Skip to content
Snippets Groups Projects
Select Git revision
  • 039abd604e63a633032f106b2a7fbba310fec657
  • master default protected
  • gitkeep
  • dev protected
  • Issue/2583-treeBug
  • Hotfix/2504-formGen
  • Issue/2309-docs
  • Issue/2462-removeTraces
  • Hotfix/2459-EncodingPath
  • Hotfix/2452-linkedDeletion
  • Issue/1792-newMetadataStructure
  • Hotfix/2384-guestsAndLinked
  • v2.8.14-Hotfix2365
  • Hotfix/2365-targetClassWorks
  • Hotfix/2371-fixGitLabinRCV
  • Fix/xxxx-activateGitlab
  • Test/xxxx-enablingGitLab
  • Issue/2349-gitlabHttps
  • Issue/2287-guestRole
  • Hotfix/2296-selectedValuesNotReturned
  • Issue/2102-gitLabResTypeRCV
  • v2.11.5
  • v2.11.4
  • v2.11.3
  • v2.11.2
  • v2.11.1
  • v2.11.0
  • v2.10.3
  • v2.10.2
  • v2.10.1
  • v2.10.0
  • v2.9.4
  • v2.9.3
  • v2.9.2
  • v2.9.1
  • v2.9.0
  • v2.8.16
  • v2.8.15
  • v2.8.14
  • v2.8.13
  • v2.8.12
41 results

TreeController.cs

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    TreeController.cs 14.09 KiB
    using Coscine.Api.WaterbutlerHelper;
    using Coscine.Api.WaterbutlerHelper.ReturnObjects;
    using Coscine.Api.WaterbutlerHelper.Services;
    using Coscine.ApiCommons;
    using Coscine.ApiCommons.Factories;
    using Coscine.Database.DataModel;
    using Coscine.Database.Models;
    using Coscine.Database.Util;
    using Coscine.Logging;
    using Coscine.Metadata;
    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.Text.RegularExpressions;
    using System.Threading.Tasks;
    using System.Web;
    using VDS.RDF;
    using VDS.RDF.Parsing;
    using VDS.RDF.Writing;
    
    namespace Coscine.Api.Tree.Controllers
    {
        /// <summary>
        /// This controller represents the actions which can be taken with a tree object.
        /// </summary>
        [Authorize]
        public class TreeController : Controller
        {
            private readonly Authenticator _authenticator;
            private readonly ResourceModel _resourceModel;
            private readonly RdfStoreConnector _rdfStoreConnector;
            private readonly ProjectRoleModel _projectRoleModel;
            private readonly ProjectResourceModel _projectResourceModel;
            private readonly CoscineLogger _coscineLogger;
            private readonly AnalyticsLogObject _analyticsLogObject;
            private readonly IDataSourceService _dataSourceService;
            private readonly WaterbutlerInterface _waterbutlerInterface;
            private readonly string _blobApiLink;
            private readonly string _prefix;
    
            /// <summary>
            /// Tree controller constructor
            /// </summary>
            /// <param name="logger">Logger</param>
            /// <param name="dataSourceService">Source Service for data</param>
            public TreeController(ILogger<TreeController> logger, IDataSourceService dataSourceService)
            {
                _authenticator = new Authenticator(this, Program.Configuration);
                _resourceModel = new ResourceModel();
                _rdfStoreConnector = new RdfStoreConnector(Program.Configuration.GetStringAndWait("coscine/local/virtuoso/additional/url"));
                _projectRoleModel = new ProjectRoleModel();
                _projectResourceModel = new ProjectResourceModel();
    
                _coscineLogger = new CoscineLogger(logger);
                _analyticsLogObject = new AnalyticsLogObject();
                _dataSourceService = dataSourceService;
                _waterbutlerInterface = new WaterbutlerInterface(Program.Configuration, _dataSourceService);
    
                var rule = Program.Configuration.GetStringAndWait("traefik/frontends/Coscine.Api.Blob/routes/Coscine.Api.Blob/rule");
                var host = rule.Substring("Host:".Length, rule.IndexOf(";") - "Host:".Length);
                var path = rule.Substring(rule.IndexOf("/"));
                _blobApiLink = $"https://{host}{path}/blob/";
                _prefix = Program.Configuration.GetStringAndWait("coscine/global/epic/prefix");
            }
    
            /// <summary>
            /// Generates Id
            /// </summary>
            /// <param name="resourceId">Id of the resource</param>
            /// <param name="path"> Path to file</param>
            /// <returns> Uri </returns>
            public Uri GenerateId(string resourceId, string path)
            {
                return new Uri($"https://hdl.handle.net/{_prefix}/{resourceId}@path={Uri.EscapeDataString(path)}");
            }
    
            /// <summary>
            /// This method retrieves the metadata
            /// </summary>
            /// <param name="resourceId"> Id of a resource</param>
            /// <param name="path">Path to the file</param>
            /// <returns> JSON Object with the metadata if ok, otherwise Statuscode 400 or 401 or 404</returns>
            [HttpGet("[controller]/{resourceId}/{*path}")]
            public async Task<IActionResult> GetMetadata(string resourceId, string path)
            {
                path = $"/{path}";
                if (path.Contains("%2F") || path.Contains("%2f"))
                {
                    return BadRequest("Path can not contain the sequence %2F.");
                }
    
                var user = _authenticator.GetUser();
                var check = CheckResourceIdAndPath(resourceId, path, out Resource resource);
    
                if (user == null || !_resourceModel.HasAccess(user, resource, UserRoles.Owner, UserRoles.Member))
                {
                    return Forbid("User has no Access to this resource.");
                }
    
                if (check != null)
                {
                    return check;
                }
    
                var authHeader = _waterbutlerInterface.BuildAuthHeader(resource);
    
                if (authHeader == null)
                {
                    return BadRequest($"No provider for: \"{resource.Type.DisplayName}\".");
                }
    
                var provider = GetResourceTypeName(resource);
                var infos = await _waterbutlerInterface.GetObjectInfoAsync(path, provider, authHeader);
    
                // Not found
                if (infos == null)
                {
                    return NotFound($"Found nothing in waterbutler under \"{path}\".");
                }
    
                var graphs = new List<JToken>();
    
                foreach (var info in infos)
                {
                    var id = GenerateId(resourceId, info.Path);
                    if (_rdfStoreConnector.HasGraph(id.AbsoluteUri))
                    {
                        var graph = _rdfStoreConnector.GetGraph(id);
                        graphs.Add(JToken.Parse(VDS.RDF.Writing.StringWriter.Write(graph, new RdfJsonWriter())));
                    }
                }
    
                var jObject = new JObject(
                    new JProperty("data", new JObject(
                        new JProperty("metadataStorage", JToken.FromObject(graphs)),
                        new JProperty("fileStorage", JToken.FromObject(infos.Select(x => new ObjectMetaInfoReturnObject(x, _blobApiLink, resource.Id.ToString()))))
                        ))
                    );
    
                LogAnalytics("View MD", resourceId, path, user);
                return Json(jObject);
            }
    
            /// <summary>
            /// This method stores the metadata of the file
            /// </summary>
            /// <param name="resourceId">Id of the resource</param>
            /// <param name="path">Path to the file</param>
            /// <returns>If ok Statuscode 204, otherwise Statuscode 400 or 401</returns>
            [HttpPut("[controller]/{resourceId}/{*path}")]
            public IActionResult StoreMetadataForFile(string resourceId, string path)
            {
                path = $"/{path}";
                if (path.Contains("%2F") || path.Contains("%2f"))
                {
                    return BadRequest("Path can not contain the sequence %2F.");
                }
    
                var innerBlock = ObjectFactory<JToken>.DeserializeFromStream(Request.Body);
                var graphNameUri = GenerateId(resourceId, path);
    
                var json = new JObject
                {
                    [graphNameUri.AbsoluteUri] = innerBlock
                };
    
                var user = _authenticator.GetUser();
                var resource = _resourceModel.GetById(Guid.Parse(resourceId));
    
                if (user == null || !_resourceModel.HasAccess(user, resource, UserRoles.Owner, UserRoles.Member))
                {
                    return Forbid("User is no project member!");
                }
    
                if (resource.ApplicationProfile[resource.ApplicationProfile.Length - 1] != '/')
                {
                    resource.ApplicationProfile += '/';
                }
                json[graphNameUri.AbsoluteUri]["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"] = new JArray
                    {
                        new JObject
                        {
                            ["value"] = resource.ApplicationProfile,
                            ["type"] = "uri"
                        }
                    };
    
                // throw bad request if empty node value is detected
                JToken root = json.First.First;
                foreach (var node in root)
                {
                    string nodeValue = node.First.First["value"].ToString().ToLower();
                    if (string.IsNullOrEmpty(nodeValue))
                    {
                        return BadRequest("Empty values in application profile are not accepted.");
                    }
                }
    
                var graph = new Graph();
                graph.LoadFromString(json.ToString(), new RdfJsonParser());
    
                var fixedValuesGraph = new Graph();
                fixedValuesGraph.LoadFromString(resource.FixedValues, new RdfJsonParser());
    
                var shapesGraph = (Graph)_rdfStoreConnector.GetGraph(resource.ApplicationProfile);
    
                foreach (var triple in fixedValuesGraph.Triples.Where(x => x.Predicate.ToString() == "https://purl.org/coscine/fixedValue"))
                {
                    var shapeTriples = shapesGraph.Triples.Where((shapeTriple) =>
                        shapeTriple.Subject.ToString() == triple.Subject.ToString()
                        && shapeTriple.Predicate.ToString() == "http://www.w3.org/ns/shacl#path");
    
                    var entry = shapeTriples.First();
    
                    // Remove any existing triples
                    foreach (var triple2 in graph.GetTriplesWithSubjectPredicate(graph.CreateUriNode(graphNameUri), entry.Object).ToList())
                    {
                        graph.Retract(triple2);
                    }
    
                    var tripleObject = triple.Object;
                    var tripleObjectString = tripleObject.ToString();
                    if (tripleObjectString == "{ME}")
                    {
                        tripleObjectString = tripleObjectString.Replace("{ME}", user.DisplayName);
                        tripleObject = graph.CreateLiteralNode(tripleObjectString, new Uri(XmlSpecsHelper.XmlSchemaDataTypeString));
                    }
                    else if (tripleObjectString == "{TODAY}")
                    {
                        tripleObjectString = tripleObjectString.Replace("{TODAY}", DateTime.Today.ToString("yyyy-MM-dd"));
                        tripleObject = graph.CreateLiteralNode(tripleObjectString, new Uri(XmlSpecsHelper.XmlSchemaDataTypeDate));
                    }
    
                    graph.Assert(graph.CreateUriNode(graphNameUri), entry.Object, tripleObject);
                }
    
                // Default values is not checked or added
    
                // validate the data
                if (!_rdfStoreConnector.ValidateShacl(graph, graphNameUri))
                {
                    return BadRequest("Data has the wrong format!");
                }
                // store the data
                if (_rdfStoreConnector.HasGraph(graphNameUri))
                {
                    _rdfStoreConnector.ClearGraph(graphNameUri);
                    LogAnalytics("Update MD", resourceId, path, user);
                }
                else
                {
                    _rdfStoreConnector.CreateNamedGraph(graphNameUri);
                    LogAnalytics("Upload MD", resourceId, path, user);
                }
    
                // BaseUri must be set for the sparql query
                graph.BaseUri = graphNameUri;
                _rdfStoreConnector.AddGraph(graph);
    
                return NoContent();
    
            }
            /// <summary>
            /// Checks the resource Id and the path
            /// </summary>
            /// <param name="resourceId">Id of the resource</param>
            /// <param name="path">Path to the file</param>
            /// <param name="resource">Resource</param>
            /// <returns>null, otherwise Statuscode  400 or 404 </returns>
            private IActionResult CheckResourceIdAndPath(string resourceId, string path, out Resource resource)
            {
                resource = null;
    
                if (string.IsNullOrWhiteSpace(path))
                {
                    return BadRequest($"Your path \"{path}\" is empty.");
                }
    
                Regex rgx = new Regex(@"[\:?*<>|]+");
                if (rgx.IsMatch(path))
                {
                    return BadRequest($"Your path \"{path}\" contains bad characters. The following characters are not permissible: {@"\/:?*<>|"}.");
                }
    
                if (!Guid.TryParse(resourceId, out Guid resourceGuid))
                {
                    return BadRequest($"{resourceId} is not a guid.");
                }
    
                try
                {
                    resource = _resourceModel.GetById(resourceGuid);
                    if (resource == null)
                    {
                        return NotFound($"Could not find resource with id: {resourceId}");
                    }
                }
                catch (Exception)
                {
                    return NotFound($"Could not find resource with id: {resourceId}");
                }
    
                if (resource.Type == null)
                {
                    ResourceTypeModel resourceTypeModel = new ResourceTypeModel();
                    resource.Type = resourceTypeModel.GetById(resource.TypeId);
                }
    
                // All good
                return null;
            }
            /// <summary>
            /// Gets the name of the resource type
            /// </summary>
            /// <param name="resource">Resource</param>
            /// <returns> Processed name of the resource </returns>
            private string GetResourceTypeName(Resource resource)
            {
                if (resource.Type.DisplayName.ToLower().Equals("s3"))
                {
                    return "rds";
                }
                else
                {
                    return resource.Type.DisplayName.ToLower();
                }
            }
            /// <summary>
            /// Log analytics
            /// </summary>
            /// <param name="operation">Operation</param>
            /// <param name="resourceId">Resource of the id</param>
            /// <param name="filename">Name of the file</param>
            /// <param name="user">User object</param>
            private void LogAnalytics(string operation, string resourceId, string filename, User user)
            {
                if (CoscineLoggerConfiguration.IsLogLevelActivated(LogType.Analytics))
                {
                    _analyticsLogObject.Type = "Action";
                    _analyticsLogObject.Operation = operation;
                    _analyticsLogObject.FileId = resourceId + "/" + HttpUtility.UrlDecode(filename).Substring(1);
                    _analyticsLogObject.ResourceId = resourceId;
                    _analyticsLogObject.ProjectId = _projectResourceModel.GetProjectForResource(new Guid(resourceId)).ToString();
                    _analyticsLogObject.RoleId = _projectRoleModel.GetGetUserRoleForProject(new Guid(_analyticsLogObject.ProjectId), user.Id).ToString();
                    _coscineLogger.AnalyticsLog(_analyticsLogObject);
                }
            }
        }
    }