From 0be7aea27a5e9529e37800871b05ca36989fb526 Mon Sep 17 00:00:00 2001 From: Marcel Nellesen <nellesen@itc.rwth-aachen.de> Date: Mon, 3 Feb 2020 10:49:54 +0100 Subject: [PATCH] Update: query for all top-most accessible projects (coscine/issues#602) Update: Return the parent Id if accessible (coscine/issues#607) New: Enabled flags for resource types (coscine/issues#611) Fix: fix special characters --- src/Project.Tests/Project.Tests.csproj | 12 +++--- src/Project.Tests/ResourceControllerTests.cs | 2 +- src/Project.Tests/app.config | 2 +- src/Project.Tests/packages.config | 6 +-- src/Project/App.config | 2 +- .../Controllers/DataSourceController.cs | 38 +++++++++---------- src/Project/Controllers/ProjectController.cs | 12 +++++- src/Project/Controllers/ResourceController.cs | 6 +++ .../Controllers/ResourceTypeController.cs | 9 ++++- .../Controllers/SubProjectController.cs | 13 ++++++- src/Project/Models/ProjectModel.cs | 16 +++++++- src/Project/Models/ResourceModel.cs | 2 +- src/Project/Project.csproj | 12 +++--- .../ReturnObjects/ResourceTypeObject.cs | 5 ++- src/Project/packages.config | 6 +-- 15 files changed, 94 insertions(+), 49 deletions(-) diff --git a/src/Project.Tests/Project.Tests.csproj b/src/Project.Tests/Project.Tests.csproj index 2c54126..7449eb9 100644 --- a/src/Project.Tests/Project.Tests.csproj +++ b/src/Project.Tests/Project.Tests.csproj @@ -56,8 +56,8 @@ <Reference Include="Consul, Version=0.7.2.6, Culture=neutral, PublicKeyToken=20a6ad9a81df1d95, processorArchitecture=MSIL"> <HintPath>..\packages\Consul.0.7.2.6\lib\net45\Consul.dll</HintPath> </Reference> - <Reference Include="Coscine.Action, Version=1.7.1.0, Culture=neutral, processorArchitecture=AMD64"> - <HintPath>..\packages\Coscine.Action.1.7.1\lib\net461\Coscine.Action.dll</HintPath> + <Reference Include="Coscine.Action, Version=1.8.0.0, Culture=neutral, processorArchitecture=AMD64"> + <HintPath>..\packages\Coscine.Action.1.8.0\lib\net461\Coscine.Action.dll</HintPath> </Reference> <Reference Include="Coscine.ApiCommons, Version=1.4.0.0, Culture=neutral, PublicKeyToken=af4c1345df96546b, processorArchitecture=MSIL"> <HintPath>..\packages\Coscine.ApiCommons.1.4.0\lib\net461\Coscine.ApiCommons.dll</HintPath> @@ -65,14 +65,14 @@ <Reference Include="Coscine.Configuration, Version=1.4.0.0, Culture=neutral, PublicKeyToken=ce3d7a32d7dc1e5a, processorArchitecture=MSIL"> <HintPath>..\packages\Coscine.Configuration.1.4.0\lib\net461\Coscine.Configuration.dll</HintPath> </Reference> - <Reference Include="Coscine.Database, Version=1.13.0.0, Culture=neutral, PublicKeyToken=767d77427707b70a, processorArchitecture=MSIL"> - <HintPath>..\packages\Coscine.Database.1.13.0\lib\net461\Coscine.Database.dll</HintPath> + <Reference Include="Coscine.Database, Version=1.14.0.0, Culture=neutral, PublicKeyToken=767d77427707b70a, processorArchitecture=MSIL"> + <HintPath>..\packages\Coscine.Database.1.14.0\lib\net461\Coscine.Database.dll</HintPath> </Reference> <Reference Include="Coscine.Logging, Version=1.0.1.0, Culture=neutral, PublicKeyToken=e1ed402bc3f6525e, processorArchitecture=MSIL"> <HintPath>..\packages\Coscine.Logging.1.0.1\lib\net461\Coscine.Logging.dll</HintPath> </Reference> - <Reference Include="Coscine.ProxyApi, Version=1.2.0.0, Culture=neutral, processorArchitecture=MSIL"> - <HintPath>..\packages\Coscine.ProxyApi.1.2.0\lib\net461\Coscine.ProxyApi.dll</HintPath> + <Reference Include="Coscine.ProxyApi, Version=1.2.1.0, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\Coscine.ProxyApi.1.2.1\lib\net461\Coscine.ProxyApi.dll</HintPath> </Reference> <Reference Include="Coscine.SharePoint.Webparts.Vue, Version=1.4.0.0, Culture=neutral, PublicKeyToken=0fe8d3e516df6d98, processorArchitecture=MSIL"> <HintPath>..\packages\Coscine.SharePoint.Webparts.Vue.1.4.0\lib\net461\Coscine.SharePoint.Webparts.Vue.dll</HintPath> diff --git a/src/Project.Tests/ResourceControllerTests.cs b/src/Project.Tests/ResourceControllerTests.cs index b69ef09..e728df4 100644 --- a/src/Project.Tests/ResourceControllerTests.cs +++ b/src/Project.Tests/ResourceControllerTests.cs @@ -86,7 +86,7 @@ namespace Coscine.Api.Project.Tests "testD", "keys", "usageR", - new ResourceTypeObject(Resources[0].Type.Id, Resources[0].Type.DisplayName), + new ResourceTypeObject(Resources[0].Type.Id, Resources[0].Type.DisplayName, Resources[0].Type.Enabled), new List<DisciplineObject>() { new DisciplineObject(Discipline.Id, Discipline.Url, Discipline.DisplayNameDe, Discipline.DisplayNameEn) }, new VisibilityObject(Visibility.Id, Visibility.DisplayName), new LicenseObject(License.Id, License.DisplayName), diff --git a/src/Project.Tests/app.config b/src/Project.Tests/app.config index 8187397..ee00d48 100644 --- a/src/Project.Tests/app.config +++ b/src/Project.Tests/app.config @@ -88,7 +88,7 @@ </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Coscine.Database" publicKeyToken="767d77427707b70a" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-1.13.0.0" newVersion="1.13.0.0" /> + <bindingRedirect oldVersion="0.0.0.0-1.14.0.0" newVersion="1.14.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.IdentityModel.Tokens.Jwt" publicKeyToken="31bf3856ad364e35" culture="neutral" /> diff --git a/src/Project.Tests/packages.config b/src/Project.Tests/packages.config index 034f6f4..38b7994 100644 --- a/src/Project.Tests/packages.config +++ b/src/Project.Tests/packages.config @@ -4,12 +4,12 @@ <package id="AutoMapper.Extensions.Microsoft.DependencyInjection" version="6.0.0" targetFramework="net472" /> <package id="Castle.Core" version="4.4.0" targetFramework="net472" /> <package id="Consul" version="0.7.2.6" targetFramework="net472" /> - <package id="Coscine.Action" version="1.7.1" targetFramework="net472" /> + <package id="Coscine.Action" version="1.8.0" targetFramework="net472" /> <package id="Coscine.ApiCommons" version="1.4.0" targetFramework="net472" /> <package id="Coscine.Configuration" version="1.4.0" targetFramework="net472" /> - <package id="Coscine.Database" version="1.13.0" targetFramework="net472" /> + <package id="Coscine.Database" version="1.14.0" targetFramework="net472" /> <package id="Coscine.Logging" version="1.0.1" targetFramework="net472" /> - <package id="Coscine.ProxyApi" version="1.2.0" targetFramework="net472" /> + <package id="Coscine.ProxyApi" version="1.2.1" targetFramework="net472" /> <package id="Coscine.SharePoint.Webparts.Vue" version="1.4.0" targetFramework="net472" /> <package id="dotNetRDF" version="2.2.1" targetFramework="net472" /> <package id="dotNetRDF.Data.Virtuoso" version="2.2.1" targetFramework="net472" /> diff --git a/src/Project/App.config b/src/Project/App.config index bf476cf..02589e5 100644 --- a/src/Project/App.config +++ b/src/Project/App.config @@ -91,7 +91,7 @@ </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Coscine.Database" publicKeyToken="767d77427707b70a" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-1.13.0.0" newVersion="1.13.0.0" /> + <bindingRedirect oldVersion="0.0.0.0-1.14.0.0" newVersion="1.14.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.IdentityModel.Tokens.Jwt" publicKeyToken="31bf3856ad364e35" culture="neutral" /> diff --git a/src/Project/Controllers/DataSourceController.cs b/src/Project/Controllers/DataSourceController.cs index 7dd77b6..8eb0c26 100644 --- a/src/Project/Controllers/DataSourceController.cs +++ b/src/Project/Controllers/DataSourceController.cs @@ -59,10 +59,7 @@ namespace Coscine.Api.Project.Controllers { var user = _authenticator.GetUser(); - if (!string.IsNullOrWhiteSpace(path)) - { - path = HttpUtility.UrlDecode(path); - } + path = FormatPath(path); var check = CheckResourceIdAndPath(resourceId, path, out Resource resource); if (check != null) @@ -120,11 +117,7 @@ namespace Coscine.Api.Project.Controllers { var user = _authenticator.GetUser(); - - if (!string.IsNullOrWhiteSpace(path)) - { - path = HttpUtility.UrlDecode(path); - } + path = FormatPath(path); var check = CheckResourceIdAndPath(resourceId, path, out Resource resource); if (check != null) @@ -177,10 +170,7 @@ namespace Coscine.Api.Project.Controllers { var user = _authenticator.GetUser(); - if (!string.IsNullOrWhiteSpace(path)) - { - path = HttpUtility.UrlDecode(path); - } + path = FormatPath(path); var check = CheckResourceIdAndPath(resourceId, path, out Resource resource); if (check != null) @@ -224,6 +214,17 @@ namespace Coscine.Api.Project.Controllers } } + private string FormatPath(string path) + { + if (!string.IsNullOrWhiteSpace(path)) + { + path = HttpUtility.UrlDecode(path); + path = path.Replace(@"\", "/"); + } + + return path; + } + private string GetResourceTypeName(Resource resource) { if (resource.Type.DisplayName.ToLower().Equals("s3")) { @@ -261,10 +262,7 @@ namespace Coscine.Api.Project.Controllers { var user = _authenticator.GetUser(); - if (!string.IsNullOrWhiteSpace(path)) - { - path = HttpUtility.UrlDecode(path); - } + path = FormatPath(path); var check = CheckResourceIdAndPath(resourceId, path, out Resource resource); if (check != null) @@ -398,10 +396,10 @@ namespace Coscine.Api.Project.Controllers return BadRequest($"Your path \"{path}\" is empty."); } - Regex rgx = new Regex(@"^[0-9a-zA-Z_\-/. ]+$"); - if (!rgx.IsMatch(path)) + Regex rgx = new Regex(@"[\:?*<>|]+"); + if (rgx.IsMatch(path)) { - return BadRequest($"Your path \"{path}\" contains bad chars. Only {@"^[0-9a-zA-Z_\-./ ]+"} are allowed as chars."); + return BadRequest($"Your path \"{path}\" contains bad characters. The following characters are not permissible: {@"\/:?*<>|"}."); } if (!Guid.TryParse(resourceId, out Guid resourceGuid)) diff --git a/src/Project/Controllers/ProjectController.cs b/src/Project/Controllers/ProjectController.cs index a6dad85..1ced64d 100644 --- a/src/Project/Controllers/ProjectController.cs +++ b/src/Project/Controllers/ProjectController.cs @@ -19,7 +19,7 @@ namespace Coscine.Api.Project.Controllers private readonly Authenticator _authenticator; private readonly ProjectModel _projectModel; private readonly IConfiguration _configuration; - private readonly Emitter _emitter; + private readonly Emitter _emitter; public ProjectController() { @@ -48,6 +48,14 @@ namespace Coscine.Api.Project.Controllers var project = _projectModel.GetById(Guid.Parse(id)); if (_projectModel.HasAccess(user, project, UserRoles.Member, UserRoles.Owner)) { + SubProjectModel subProjectModel = new SubProjectModel(); + var subProjectRel = subProjectModel.GetAllWhere((subProject) => subProject.SubProjectId == project.Id); + + var parentProjectRelation = subProjectRel.FirstOrDefault(); + if (parentProjectRelation != null && _projectModel.HasAccess(user, parentProjectRelation.ProjectId, UserRoles.Member, UserRoles.Owner)) + { + return Ok(_projectModel.CreateReturnObjectFromDatabaseObject(project, parentProjectRelation.ProjectId)); + } return Ok(_projectModel.CreateReturnObjectFromDatabaseObject(project)); } else @@ -176,7 +184,7 @@ namespace Coscine.Api.Project.Controllers var project = _projectModel.StoreFromObject(projectObject, user); - if (projectObject.ParentId != null + if (projectObject.ParentId != null && projectObject.ParentId != new Guid() // for now, only an owner can add subprojects to projects && _projectModel.HasAccess(user, _projectModel.GetById(projectObject.ParentId), UserRoles.Owner)) diff --git a/src/Project/Controllers/ResourceController.cs b/src/Project/Controllers/ResourceController.cs index 9ca1ae6..bee1704 100644 --- a/src/Project/Controllers/ResourceController.cs +++ b/src/Project/Controllers/ResourceController.cs @@ -118,11 +118,17 @@ namespace Coscine.Api.Project.Controllers { var resourceObject = ObjectFactory<ResourceObject>.DeserializeFromStream(Request.Body); var projectModel = new ProjectModel(); + var resourceTypeModel = new ResourceTypeModel(); + var isResourceEnabled = resourceTypeModel.GetById(resourceObject.Type.Id).Enabled; var project = projectModel.GetById(Guid.Parse(projectId)); var user = _authenticator.GetUser(); if (projectModel.HasAccess(user, project, UserRoles.Owner, UserRoles.Member)) { + if (!isResourceEnabled) + { + return Unauthorized("The user is not authorized to add a new resource of this type to the selected project!"); + } resourceObject.Creator = user.Id; var resource = _resourceModel.StoreFromObject(resourceObject); projectModel.AddResource(project, resource); diff --git a/src/Project/Controllers/ResourceTypeController.cs b/src/Project/Controllers/ResourceTypeController.cs index 04e097a..4fb4237 100644 --- a/src/Project/Controllers/ResourceTypeController.cs +++ b/src/Project/Controllers/ResourceTypeController.cs @@ -25,7 +25,14 @@ namespace Coscine.Api.Project.Controllers public IActionResult Index() { return Json(_resourceTypeModel.GetAll() - .Select((resourceType) => new ResourceTypeObject(resourceType.Id, resourceType.DisplayName))); + .Select((resourceType) => new ResourceTypeObject(resourceType.Id, resourceType.DisplayName, resourceType.Enabled))); + } + + [Route("[controller]/enabled")] + public IActionResult GetEnabledResourceTypes() + { + return Json(_resourceTypeModel.GetAllWhere((resourceType) => (resourceType.Enabled == true)) + .Select((resourceType) => new ResourceTypeObject(resourceType.Id, resourceType.DisplayName, resourceType.Enabled))); } [Route("[controller]/{id}/fields")] diff --git a/src/Project/Controllers/SubProjectController.cs b/src/Project/Controllers/SubProjectController.cs index a9f9563..812da57 100644 --- a/src/Project/Controllers/SubProjectController.cs +++ b/src/Project/Controllers/SubProjectController.cs @@ -24,10 +24,19 @@ namespace Coscine.Api.Project.Controllers { var parentGuid = new Guid(parentId); var projectModel = new ProjectModel(); + var projectRoleModel = new ProjectRoleModel(); var user = _authenticator.GetUser(); - if (projectModel.HasAccess(user, projectModel.GetById(parentGuid), UserRoles.Owner, UserRoles.Member)) + string[] allowedRoles = { UserRoles.Owner, UserRoles.Member }; + allowedRoles = allowedRoles.Select(x => x.ToLower().Trim()).ToArray(); + if (projectModel.HasAccess(user, projectModel.GetById(parentGuid), allowedRoles)) { - var subProjects = _subProjectModel.GetAllWhere((subProjectM) => (subProjectM.ProjectId == parentGuid)) + var subProjects = _subProjectModel.GetAllWhere((subProjectM) => (subProjectM.ProjectId == parentGuid + // select only subprojects to which the user has access + && (from projectRole in subProjectM.SubProject_FK.ProjectRolesProjectIdIds + where projectRole.User.Id == user.Id + && allowedRoles.Contains(projectRole.Role.DisplayName.ToLower()) + select projectRole).Any()) + ) .Select((subProject) => projectModel.GetById(subProject.SubProjectId)) .Select((project) => projectModel.CreateReturnObjectFromDatabaseObject(project, parentGuid)) .OrderBy(element => element.DisplayName); diff --git a/src/Project/Models/ProjectModel.cs b/src/Project/Models/ProjectModel.cs index db9f51d..8dff8dc 100644 --- a/src/Project/Models/ProjectModel.cs +++ b/src/Project/Models/ProjectModel.cs @@ -102,6 +102,11 @@ namespace Coscine.Api.Project.Models return projectRole; } + public bool HasAccess(User user, Guid projectId, params string[] allowedAccess) + { + return HasAccess(user, GetById(projectId), allowedAccess); + } + public bool HasAccess(User user, Database.Model.Project project, params string[] allowedAccess) { ProjectRoleModel projectRoleModel = new ProjectRoleModel(); @@ -123,7 +128,16 @@ namespace Coscine.Api.Project.Models var allUserProjectRoles = projectRoleModel.GetAllWhere((projectRoleRelation) => projectRoleRelation.UserId == user.Id && allowedAccess.Contains(projectRoleRelation.Role.DisplayName.ToLower())); var allowedProjectIds = allUserProjectRoles.Select((projectRole) => projectRole.ProjectId); - var allowedProjects = projectModel.GetAllWhere((project) => allowedProjectIds.Contains(project.Id)); + var allowedProjects = projectModel.GetAllWhere((project) => allowedProjectIds.Contains(project.Id) + && ((!project.SubProjectsSubProjectIdIds.Any()) // get top level projects not having any parent projects + || !(from subProject in project.SubProjectsSubProjectIdIds // check if the direct parent project is accessible to the current user + where (from parentProjectRole in subProject.Project.ProjectRolesProjectIdIds + where parentProjectRole.UserId == user.Id + && allowedAccess.Contains(parentProjectRole.Role.DisplayName.ToLower()) + select parentProjectRole).Any() + select subProject).Any()) + ); + return allowedProjects.ToList(); } diff --git a/src/Project/Models/ResourceModel.cs b/src/Project/Models/ResourceModel.cs index e3b5527..a44a64d 100644 --- a/src/Project/Models/ResourceModel.cs +++ b/src/Project/Models/ResourceModel.cs @@ -322,7 +322,7 @@ namespace Coscine.Api.Project.Models resource.Description, resource.Keywords, resource.UsageRights, - new ResourceTypeObject(resource.Type.Id, resource.Type.DisplayName), + new ResourceTypeObject(resource.Type.Id, resource.Type.DisplayName, resource.Type.Enabled), disciplines, (resource.Visibility != null) ? new VisibilityObject(resource.Visibility.Id, resource.Visibility.DisplayName) : null, (resource.License != null) ? new LicenseObject(resource.License.Id, resource.License.DisplayName) : null, diff --git a/src/Project/Project.csproj b/src/Project/Project.csproj index 309ead7..a8b92dd 100644 --- a/src/Project/Project.csproj +++ b/src/Project/Project.csproj @@ -46,8 +46,8 @@ <Reference Include="Consul, Version=0.7.2.6, Culture=neutral, PublicKeyToken=20a6ad9a81df1d95, processorArchitecture=MSIL"> <HintPath>..\packages\Consul.0.7.2.6\lib\net45\Consul.dll</HintPath> </Reference> - <Reference Include="Coscine.Action, Version=1.7.1.0, Culture=neutral, processorArchitecture=AMD64"> - <HintPath>..\packages\Coscine.Action.1.7.1\lib\net461\Coscine.Action.dll</HintPath> + <Reference Include="Coscine.Action, Version=1.8.0.0, Culture=neutral, processorArchitecture=AMD64"> + <HintPath>..\packages\Coscine.Action.1.8.0\lib\net461\Coscine.Action.dll</HintPath> </Reference> <Reference Include="Coscine.ApiCommons, Version=1.4.0.0, Culture=neutral, PublicKeyToken=af4c1345df96546b, processorArchitecture=MSIL"> <HintPath>..\packages\Coscine.ApiCommons.1.4.0\lib\net461\Coscine.ApiCommons.dll</HintPath> @@ -55,14 +55,14 @@ <Reference Include="Coscine.Configuration, Version=1.4.0.0, Culture=neutral, PublicKeyToken=ce3d7a32d7dc1e5a, processorArchitecture=MSIL"> <HintPath>..\packages\Coscine.Configuration.1.4.0\lib\net461\Coscine.Configuration.dll</HintPath> </Reference> - <Reference Include="Coscine.Database, Version=1.13.0.0, Culture=neutral, PublicKeyToken=767d77427707b70a, processorArchitecture=MSIL"> - <HintPath>..\packages\Coscine.Database.1.13.0\lib\net461\Coscine.Database.dll</HintPath> + <Reference Include="Coscine.Database, Version=1.14.0.0, Culture=neutral, PublicKeyToken=767d77427707b70a, processorArchitecture=MSIL"> + <HintPath>..\packages\Coscine.Database.1.14.0\lib\net461\Coscine.Database.dll</HintPath> </Reference> <Reference Include="Coscine.Logging, Version=1.0.1.0, Culture=neutral, PublicKeyToken=e1ed402bc3f6525e, processorArchitecture=MSIL"> <HintPath>..\packages\Coscine.Logging.1.0.1\lib\net461\Coscine.Logging.dll</HintPath> </Reference> - <Reference Include="Coscine.ProxyApi, Version=1.2.0.0, Culture=neutral, processorArchitecture=MSIL"> - <HintPath>..\packages\Coscine.ProxyApi.1.2.0\lib\net461\Coscine.ProxyApi.dll</HintPath> + <Reference Include="Coscine.ProxyApi, Version=1.2.1.0, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\Coscine.ProxyApi.1.2.1\lib\net461\Coscine.ProxyApi.dll</HintPath> </Reference> <Reference Include="Coscine.SharePoint.Webparts.Vue, Version=1.4.0.0, Culture=neutral, PublicKeyToken=0fe8d3e516df6d98, processorArchitecture=MSIL"> <HintPath>..\packages\Coscine.SharePoint.Webparts.Vue.1.4.0\lib\net461\Coscine.SharePoint.Webparts.Vue.dll</HintPath> diff --git a/src/Project/ReturnObjects/ResourceTypeObject.cs b/src/Project/ReturnObjects/ResourceTypeObject.cs index e2b9130..63e26af 100644 --- a/src/Project/ReturnObjects/ResourceTypeObject.cs +++ b/src/Project/ReturnObjects/ResourceTypeObject.cs @@ -10,10 +10,13 @@ namespace Coscine.Api.Project.ReturnObjects public string DisplayName { get; set; } - public ResourceTypeObject(Guid id, string displayName) + public bool Enabled { get; set; } + + public ResourceTypeObject(Guid id, string displayName, bool enabled) { Id = id; DisplayName = displayName; + Enabled = enabled; } } diff --git a/src/Project/packages.config b/src/Project/packages.config index 0ef590e..7ad9f14 100644 --- a/src/Project/packages.config +++ b/src/Project/packages.config @@ -3,12 +3,12 @@ <package id="AutoMapper" version="8.0.0" targetFramework="net472" /> <package id="AutoMapper.Extensions.Microsoft.DependencyInjection" version="6.0.0" targetFramework="net472" /> <package id="Consul" version="0.7.2.6" targetFramework="net472" /> - <package id="Coscine.Action" version="1.7.1" targetFramework="net472" /> + <package id="Coscine.Action" version="1.8.0" targetFramework="net472" /> <package id="Coscine.ApiCommons" version="1.4.0" targetFramework="net472" /> <package id="Coscine.Configuration" version="1.4.0" targetFramework="net472" /> - <package id="Coscine.Database" version="1.13.0" targetFramework="net472" /> + <package id="Coscine.Database" version="1.14.0" targetFramework="net472" /> <package id="Coscine.Logging" version="1.0.1" targetFramework="net472" /> - <package id="Coscine.ProxyApi" version="1.2.0" targetFramework="net472" /> + <package id="Coscine.ProxyApi" version="1.2.1" targetFramework="net472" /> <package id="Coscine.SharePoint.Webparts.Vue" version="1.4.0" targetFramework="net472" /> <package id="dotNetRDF" version="2.2.1" targetFramework="net472" /> <package id="dotNetRDF.Data.Virtuoso" version="2.2.1" targetFramework="net472" /> -- GitLab