ProjectController.cs 19.5 KB
Newer Older
1
using Coscine.Action;
2
using Coscine.Action.EventArgs;
3
4
using Coscine.ApiCommons;
using Coscine.ApiCommons.Factories;
5
using Coscine.Configuration;
6
7
8
using Coscine.Database.DataModel;
using Coscine.Database.Models;
using Coscine.Database.ReturnObjects;
Marcel Nellesen's avatar
Marcel Nellesen committed
9
using Coscine.Database.Util;
10
using Coscine.Logging;
11
12
13
14
using Coscine.Metadata;
using Coscine.ResourceLoader;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
15
using Microsoft.Extensions.Logging;
16
using System;
17
using System.Collections.Generic;
18
using System.Linq;
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
19
20
21

namespace Coscine.Api.Project.Controllers
{
22
23
24
25

    /// <summary>
    ///  /// This controller represents the actions which can be taken with a project object.
    /// </summary>
26
    [Authorize]
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
27
28
29
    public class ProjectController : Controller
    {
        private readonly Authenticator _authenticator;
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
30
        private readonly ProjectModel _projectModel;
31
        private readonly IConfiguration _configuration;
32
        private readonly Emitter _emitter;
Marcel Nellesen's avatar
Marcel Nellesen committed
33
        private readonly ActivatedFeaturesModel _activatedFeaturesModel;
34
35
36
        private readonly ProjectRoleModel _projectRoleModel;
        private readonly CoscineLogger _coscineLogger;
        private readonly AnalyticsLogObject _analyticsLogObject;
37

38
39
40
41
        /// <summary>
        /// ProjectController constructor
        /// </summary>
        /// <param name="logger">Logger</param>
42
        public ProjectController(ILogger<ProjectController> logger)
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
43
        {
44
            _authenticator = new Authenticator(this, Program.Configuration);
45
            _configuration = Program.Configuration;
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
46
            _projectModel = new ProjectModel();
47
            _emitter = new Emitter(_configuration);
Marcel Nellesen's avatar
Marcel Nellesen committed
48
            _activatedFeaturesModel = new ActivatedFeaturesModel();
49
50
51
            _projectRoleModel = new ProjectRoleModel();
            _coscineLogger = new CoscineLogger(logger);
            _analyticsLogObject = new AnalyticsLogObject();
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
52
        }
53
54
55
56
57

        /// <summary>
        /// Returns all available projects (including sub projects)
        /// </summary>
        /// <returns>Ok</returns>
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
58
        [Route("[controller]")]
59
        public ActionResult<IEnumerable<ProjectObject>> Index()
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
60
        {
61
            var user = _authenticator.GetUser();
62
            var result = _projectModel.GetWithAccess(user, UserRoles.Member, UserRoles.Owner).ToList()
63
                .Select((project) => _projectModel.CreateReturnObjectFromDatabaseObject(project))
64
65
                .OrderBy(element => element.DisplayName);

66
            if (Request.Query != null && Request.Query["noanalyticslog"] != "true")
67
68
69
70
71
72
73
            {
                LogAnalytics("List Projects", result);
            }

            return Ok(result);
        }

74
75
76
77
        /// <summary>
        /// Retrieves all top level projects
        /// </summary>
        /// <returns>Ok</returns>
78
        [Route("[controller]/-/topLevel")]
79
        public ActionResult<IEnumerable<ProjectObject>> GetTopLevelProjects()
80
81
82
83
84
85
86
        {
            var user = _authenticator.GetUser();
            var result = _projectModel.GetTopLevelWithAccess(user, UserRoles.Member, UserRoles.Owner).ToList()
                .Select((project) => _projectModel.CreateReturnObjectFromDatabaseObject(project))
                .OrderBy(element => element.DisplayName);

            if (Request.Query != null && Request.Query["noanalyticslog"] != "true")
87
88
89
            {
                LogAnalytics("View Home", result);
            }
90
91

            return Ok(result);
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
92
93
        }

94
95
96
97
98
        /// <summary>
        /// This returns the the project if the user has access to it
        /// </summary>
        /// <param name="id">Id of the resource</param>
        /// <returns>Ok or Statuscode 401</returns>
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
99
        [HttpGet("[controller]/{id}")]
100
        public ActionResult<ProjectObject> Get(string id)
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
101
        {
102
103
104
105
            var user = _authenticator.GetUser();
            var project = _projectModel.GetById(Guid.Parse(id));
            if (_projectModel.HasAccess(user, project, UserRoles.Member, UserRoles.Owner))
            {
106
                SubProjectModel subProjectModel = new SubProjectModel();
107
                var subProjectRel = subProjectModel.GetAllWhere((subProject) => subProject.SubProjectId == project.Id && project.Deleted == false);
108
109
110
111
112
113
                
                var parentProjectRelation = subProjectRel.FirstOrDefault();
                if (parentProjectRelation != null && _projectModel.HasAccess(user, parentProjectRelation.ProjectId, UserRoles.Member, UserRoles.Owner))
                {
                    return Ok(_projectModel.CreateReturnObjectFromDatabaseObject(project, parentProjectRelation.ProjectId));
                }
114
115
116
117
118
119
                return Ok(_projectModel.CreateReturnObjectFromDatabaseObject(project));
            }
            else
            {
                return Unauthorized($"User is not allowed to see given the project {id}");
            }
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
120
121
        }

122
123
124
125
126
        /// <summary>
        /// Gets the resources
        /// </summary>
        /// <param name="id">Id of the resource</param>
        /// <returns>Json object or Statuscode 401</returns>
127
        [HttpGet("[controller]/{id}/resources")]
128
        public ActionResult<IEnumerable<ResourceObject>> GetResources(string id)
129
        {
130
131
132
133
134
135
136
            var project = _projectModel.GetById(Guid.Parse(id));
            var user = _authenticator.GetUser();

            var resourceModel = new ResourceModel();
            var resourceTypeModel = new ResourceTypeModel();
            if (_projectModel.HasAccess(user, project, UserRoles.Member, UserRoles.Owner))
            {
137
                var resources = resourceModel.GetAllWhere((resource) =>
138
139
140
141
142
143
                        (from projectResource in resource.ProjectResourceResourceIdIds
                         where projectResource.ProjectId == project.Id
                         select projectResource).Any())
                        .Select((resource) =>
                        {
                            return resourceModel.CreateReturnObjectFromDatabaseObject(resource);
144
                        }).OrderBy(element => element.DisplayName);
145
146
147
148
                if (Request.Query != null && Request.Query["noanalyticslog"] != "true")
                {
                    LogAnalytics("View Project", null, resources, id, user); // intentionally log as view project to help identify the related user action
                }
149
                return Json(resources);
150
151
152
153
154
            }
            else
            {
                return Unauthorized($"User is not allowed to see given the project {id}");
            }
155
156
        }

157
158
159
160
161
        /// <summary>
        /// Retrieves the quota for the selected project
        /// </summary>
        /// <param name="id">Id of the resource</param>
        /// <returns>Json object or Statuscode 401</returns>
162
        [HttpGet("[controller]/{id}/quotas")]
163
        public ActionResult<IEnumerable<ProjectQuota>> Quotas(string id)
164
165
166
167
168
169
170
171
172
173
174
175
176
177
        {
            var user = _authenticator.GetUser();
            var projectObject = ObjectFactory<ProjectObject>.DeserializeFromStream(Request.Body);
            var guidId = Guid.Parse(id);
            var project = _projectModel.GetById(guidId);
            if (_projectModel.HasAccess(user, project, UserRoles.Owner))
            {
                ProjectQuotaModel projectQuotaModel = new ProjectQuotaModel();
                var projectQuotas =
                    projectQuotaModel.GetAllWhere((projectQuota) =>
                        projectQuota.ProjectId == guidId
                        && projectQuota.ResourceType.Enabled == true)
                    .Select((projectQuota) => projectQuotaModel.CreateReturnObjectFromDatabaseObject(projectQuota));

178

179
180
181
182
183
184
185
186
187
                ResourceModel resourceModel = new ResourceModel();
                RDSResourceTypeModel rdsResourceTypeModel = new RDSResourceTypeModel();
                var returnList = new List<dynamic>();
                foreach (var projectQuota in projectQuotas)
                {
                    // TODO: Cleanup quota and give it to every resource, this hard coded solution seems not scalable
                    if (projectQuota.ResourceType.DisplayName == "rds") 
                    {
                        var resources = resourceModel.GetAllWhere((resource) =>
188
189
190
191
                                            resource.TypeId == projectQuota.ResourceType.Id
                                            && (from connection in resource.ProjectResourceResourceIdIds
                                                where connection.ProjectId == guidId
                                                select connection).Any());
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
                        
                        var size = resources.Sum((resource) => 
                                rdsResourceTypeModel.GetById(resource.ResourceTypeOptionId.Value).Size);
                        returnList.Add(new { 
                            type = projectQuota.ResourceType.DisplayName, 
                            available = projectQuota.Quotas,
                            allocated = size
                        });
                    }
                }
                return Json(returnList);
            }
            else
            {
                return Unauthorized("The user is not authorized to perform a get on the selected project!");
            }
        }
209

210
211
212
213
214
        /// <summary>
        /// Updates the selected project 
        /// </summary>
        /// <param name="id">Id of the resource</param>
        /// <returns>Ok or Statuscode 401</returns>
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
215
216
217
        [HttpPost("[controller]/{id}")]
        public IActionResult Update(string id)
        {
218
219
220
            var user = _authenticator.GetUser();
            var projectObject = ObjectFactory<ProjectObject>.DeserializeFromStream(Request.Body);
            var project = _projectModel.GetById(Guid.Parse(id));
221
            if (_projectModel.HasAccess(user, project, UserRoles.Owner))
222
            {
223
                LogAnalytics("Edit Project", null, null, id, user);
224
225
226
227
228
229
                return Ok(_projectModel.UpdateByObject(project, projectObject));
            }
            else
            {
                return Unauthorized("The user is not authorized to perform an update on the selected project!");
            }
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
230
231
        }

232
233
234
235
236
        /// <summary>
        /// Deletes the selected project
        /// </summary>
        /// <param name="id">Id of the resource</param>
        /// <returns>Json object or Statuscode 401</returns>
237
238
239
        [HttpDelete("[controller]/{id}")]
        public IActionResult Delete(string id)
        {
240
241
242
243
            var user = _authenticator.GetUser();
            var project = _projectModel.GetById(Guid.Parse(id));
            if (_projectModel.HasAccess(user, project, UserRoles.Owner))
            {
244
                LogAnalytics("Delete Project", null, null, id, user);
245
246
247
248
249
250
251
                DeleteProject(project);
                return Json(_projectModel.CreateReturnObjectFromDatabaseObject(project));
            }
            else
            {
                return Unauthorized("The user is not authorized to perform an update on the selected project!");
            }
252
253
        }

254
255
256
257
258
259
        /// <summary>
        /// Deletes the project
        /// </summary>
        /// <param name="project">Project</param>
        /// <param name="isHard">isHard</param>
        /// <param name="propegateAction">propegate Action</param>
Marcel Nellesen's avatar
Marcel Nellesen committed
260
        public void DeleteProject(Database.DataModel.Project project, bool isHard = false, bool propegateAction = true)
261
        {
262
            var subProjectModel = new SubProjectModel();
263
264
265
266
            foreach (var subProject in subProjectModel.GetAllWhere(
                (subProject) => subProject.ProjectId == project.Id
                                && (subProject.SubProject_FK.Deleted == false || isHard)
            ))
267
            {
Marcel Nellesen's avatar
Marcel Nellesen committed
268
                Database.DataModel.Project subProjectObject;
269
270
271
                if (isHard)
                {
                    subProjectObject = _projectModel.GetByIdIncludingDeleted(subProject.SubProjectId);
272
                    subProjectModel.Delete(subProject);
273
274
275
276
277
278
                }
                else
                {
                    subProjectObject = _projectModel.GetById(subProject.SubProjectId);
                }
                DeleteProject(subProjectObject, isHard, propegateAction);
279
280
281
282
            }

            foreach (var subProject in subProjectModel.GetAllWhere((subProject) => subProject.SubProjectId == project.Id))
            {
283
284
285
286
                if (isHard)
                {
                    subProjectModel.Delete(subProject);
                }
287
288
            }

Marcel Nellesen's avatar
Marcel Nellesen committed
289
            if (isHard)
290
            {
Marcel Nellesen's avatar
Marcel Nellesen committed
291
                var projectResourceModel = new ProjectResourceModel();
292
293
                var resourceModel = new ResourceModel();
                var resourceTypeModel = new ResourceTypeModel();
Marcel Nellesen's avatar
Marcel Nellesen committed
294
                foreach (var projectResource in projectResourceModel.GetAllWhere((projectResource) => projectResource.ProjectId == project.Id))
295
                {
296
297
298
299
                    var resource = resourceModel.GetById(projectResource.ResourceId);
                    var resourceTypeOptions = resourceModel.GetResourceTypeOptions(projectResource.ResourceId);
                    var resourceTypeDefinition = ResourceTypeFactory.CreateResourceTypeObject(resourceTypeModel.GetById(resource.TypeId).DisplayName, _configuration);
                    resourceTypeDefinition.DeleteResource(projectResource.ResourceId.ToString(), resourceTypeOptions);
300
                    projectResourceModel.Delete(projectResource);
301
                    resourceModel.Delete(resource);
302
                }
303

Marcel Nellesen's avatar
Marcel Nellesen committed
304
305
                var projectRoleModel = new ProjectRoleModel();
                foreach (var projectRole in projectRoleModel.GetAllWhere((projectRole) => projectRole.ProjectId == project.Id))
306
307
308
                {
                    projectRoleModel.Delete(projectRole);
                }
309

Marcel Nellesen's avatar
Marcel Nellesen committed
310
311
                var projectDisciplineModel = new ProjectDisciplineModel();
                foreach (var projectDiscipline in projectDisciplineModel.GetAllWhere((projectDiscipline) => projectDiscipline.ProjectId == project.Id))
312
313
314
                {
                    projectDisciplineModel.Delete(projectDiscipline);
                }
315

Marcel Nellesen's avatar
Marcel Nellesen committed
316
317
                var projectInstituteModel = new ProjectInstituteModel();
                foreach (var projectInstitute in projectInstituteModel.GetAllWhere((projectInstitute) => projectInstitute.ProjectId == project.Id))
318
                {
Marcel Nellesen's avatar
Marcel Nellesen committed
319
320
321
322
323
324
325
                     projectInstituteModel.Delete(projectInstitute);
                }

                var projectQuotaModel = new ProjectQuotaModel();
                foreach (var projectQuota in projectQuotaModel.GetAllWhere((Quota) => Quota.ProjectId == project.Id))
                {
                    projectQuotaModel.Delete(projectQuota);
326
                }
327

Marcel Nellesen's avatar
Marcel Nellesen committed
328
329
                _activatedFeaturesModel.DeactivateAllFeatures(project);

330
331
332
333
334
335
336
                if (propegateAction)
                {
                    _emitter.EmitProjectDelete(new ProjectEventArgs(_configuration)
                    {
                        Project = project
                    });
                }
337

338
                _projectModel.HardDelete(project);
Marcel Nellesen's avatar
Marcel Nellesen committed
339
            }
340
341
342
343
            else
            {
                _projectModel.Delete(project);
            }
344
345
        }

346
347
348
349
        /// <summary>
        /// Creates a project
        /// </summary>
        /// <returns>Json object or Statuscode 401</returns>
350
        [HttpPost("[controller]")]
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
351
352
        public IActionResult Store()
        {
353
            var user = _authenticator.GetUser();
354
            var isRWTHMember = IsRWTHMember(user);
355
356
            var projectObject = ObjectFactory<ProjectObject>.DeserializeFromStream(Request.Body);

357
            if (projectObject?.ParentId != new Guid()
358
359
360
361
362
                && !_projectModel.HasAccess(user, _projectModel.GetById(projectObject.ParentId), UserRoles.Owner))
            {
                return Unauthorized("User is not allowed to create SubProjects.");
            }

363
            var project = _projectModel.StoreFromObject(projectObject, user, isRWTHMember);
364

365
            if (projectObject.ParentId != null
366
367
368
369
370
371
372
373
374
375
376
377
378
379
                && projectObject.ParentId != new Guid()
                // for now, only an owner can add subprojects to projects
                && _projectModel.HasAccess(user, _projectModel.GetById(projectObject.ParentId), UserRoles.Owner))
            {
                var subProjectModel = new SubProjectModel();
                subProjectModel.LinkSubProject(projectObject.ParentId, project.Id);
            }

            _emitter.EmitProjectCreate(new ProjectEventArgs(_configuration)
            {
                Project = project,
                ProjectOwner = user
            });

380
381
            LogAnalytics("Add Project", null, null, project.Id.ToString(), user);

382
            return Json(_projectModel.CreateReturnObjectFromDatabaseObject(project));
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
383
        }
384

385
386
387
388
389
        /// <summary>
        /// Checks if the given user is a member of the RWTH 
        /// </summary>
        /// <param name="user">User object</param>
        /// <returns>True, if member of RWTH or false, if not a member of RWTH</returns>
390
391
392
393
394
395
396
397
398
399
400
401
402
        private bool IsRWTHMember(User user)
        {
            var externalIds = new ExternalIdModel().GetAllWhere((externalId) => externalId.UserId == user.Id);
            if(externalIds.Count() == 0)
            {
                return false;
            }
            var externalIdList = new List<string>();
            
            foreach (var externalId in externalIds)
            {
                externalIdList.Add(externalId.ExternalIdColumn);
            }
403
            return new RdfStoreConnector(Program.Configuration.GetStringAndWait("coscine/local/virtuoso/additional/url")).GetTriples(new Uri("https://ror.org/04xfq0f34"), null, null, 1, externalIdList).Count() != 0;
404
405
        }

406
407
408
409
410
411
412
413
        /// <summary>
        /// LogAnalytics
        /// </summary>
        /// <param name="operation">Operation</param>
        /// <param name="projects">Projects</param>
        /// <param name="resources">Resources</param>
        /// <param name="projectId">Id of the project</param>
        /// <param name="user">User object</param>
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
        private void LogAnalytics(string operation,
            IEnumerable<ProjectObject> projects = null,
            IEnumerable<ResourceObject> resources = null,
            string projectId = null,
            User user = null
        )
        {
            if (CoscineLoggerConfiguration.IsLogLevelActivated(LogType.Analytics))
            {
                _analyticsLogObject.Type = "Action";
                _analyticsLogObject.Operation = operation;

                if (projects != null)
                {
                    List<string> projectList = new List<string>();
                    foreach (var entry in projects)
                    {
                        projectList.Add(entry.Id.ToString());
                    }
                    _analyticsLogObject.ProjectList = projectList;
                }
                if (resources != null)
                {
                    List<string> shownResources = new List<string>();
                    foreach (var entry in resources)
                    {
                        shownResources.Add(entry.Id.ToString());
                    }
                    _analyticsLogObject.ResourceList = shownResources;
                }
                if (projectId != null)
                {
                    _analyticsLogObject.ProjectId = projectId;
                    if (user != null)
                    {
                        _analyticsLogObject.RoleId = _projectRoleModel.GetGetUserRoleForProject(new Guid(_analyticsLogObject.ProjectId), user.Id).ToString();
                    }
                }
                _coscineLogger.AnalyticsLog(_analyticsLogObject);
            }
        }
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
455
456
    }
}