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

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

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

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

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

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

            return Ok(result);
        }

75
76
77
78
        /// <summary>
        /// Retrieves all top level projects
        /// </summary>
        /// <returns>Ok</returns>
79
        [Route("[controller]/-/topLevel")]
80
        public ActionResult<IEnumerable<ProjectObject>> GetTopLevelProjects()
81
82
83
84
85
86
87
        {
            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")
88
89
90
            {
                LogAnalytics("View Home", result);
            }
91
92

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

95
96
97
98
99
        /// <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
100
        [HttpGet("[controller]/{id}")]
101
        public ActionResult<ProjectObject> Get(string id)
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
102
        {
103
104
105
106
            var user = _authenticator.GetUser();
            var project = _projectModel.GetById(Guid.Parse(id));
            if (_projectModel.HasAccess(user, project, UserRoles.Member, UserRoles.Owner))
            {
107
                SubProjectModel subProjectModel = new SubProjectModel();
108
                var subProjectRel = subProjectModel.GetAllWhere((subProject) => subProject.SubProjectId == project.Id && project.Deleted == false);
109
110
111
112
113
114
                
                var parentProjectRelation = subProjectRel.FirstOrDefault();
                if (parentProjectRelation != null && _projectModel.HasAccess(user, parentProjectRelation.ProjectId, UserRoles.Member, UserRoles.Owner))
                {
                    return Ok(_projectModel.CreateReturnObjectFromDatabaseObject(project, parentProjectRelation.ProjectId));
                }
115
116
117
118
119
120
                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
121
122
        }

123
124
125
126
127
        /// <summary>
        /// Gets the resources
        /// </summary>
        /// <param name="id">Id of the resource</param>
        /// <returns>Json object or Statuscode 401</returns>
128
        [HttpGet("[controller]/{id}/resources")]
129
        public ActionResult<IEnumerable<ResourceObject>> GetResources(string id)
130
        {
131
132
133
134
135
136
137
            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))
            {
138
                var resources = resourceModel.GetAllWhere((resource) =>
139
140
141
142
143
144
                        (from projectResource in resource.ProjectResourceResourceIdIds
                         where projectResource.ProjectId == project.Id
                         select projectResource).Any())
                        .Select((resource) =>
                        {
                            return resourceModel.CreateReturnObjectFromDatabaseObject(resource);
145
                        }).OrderBy(element => element.DisplayName);
146
147
148
149
                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
                }
150
                return Json(resources);
151
152
153
154
155
            }
            else
            {
                return Unauthorized($"User is not allowed to see given the project {id}");
            }
156
157
        }

158
159
160
161
162
        /// <summary>
        /// Retrieves the quota for the selected project
        /// </summary>
        /// <param name="id">Id of the resource</param>
        /// <returns>Json object or Statuscode 401</returns>
163
        [HttpGet("[controller]/{id}/quotas")]
164
        public ActionResult<IEnumerable<ProjectQuota>> Quotas(string id)
165
166
167
168
169
170
171
172
173
174
175
176
177
178
        {
            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));

179

180
181
182
183
184
185
186
187
188
                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) =>
189
190
191
192
                                            resource.TypeId == projectQuota.ResourceType.Id
                                            && (from connection in resource.ProjectResourceResourceIdIds
                                                where connection.ProjectId == guidId
                                                select connection).Any());
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
                        
                        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!");
            }
        }
210

211
212
213
214
215
        /// <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
216
217
218
        [HttpPost("[controller]/{id}")]
        public IActionResult Update(string id)
        {
219
220
221
            var user = _authenticator.GetUser();
            var projectObject = ObjectFactory<ProjectObject>.DeserializeFromStream(Request.Body);
            var project = _projectModel.GetById(Guid.Parse(id));
222
            if (_projectModel.HasAccess(user, project, UserRoles.Owner))
223
            {
224
                LogAnalytics("Edit Project", null, null, id, user);
225
226
227
228
229
230
                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
231
232
        }

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

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

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

Marcel Nellesen's avatar
Marcel Nellesen committed
290
            if (isHard)
291
            {
Marcel Nellesen's avatar
Marcel Nellesen committed
292
293
294
                var projectResourceModel = new ProjectResourceModel();
                ResourceModel resourceModel = new ResourceModel();
                foreach (var projectResource in projectResourceModel.GetAllWhere((projectResource) => projectResource.ProjectId == project.Id))
295
296
297
298
                {
                    projectResourceModel.Delete(projectResource);
                    resourceModel.Delete(resourceModel.GetById(projectResource.ResourceId));
                }
299

Marcel Nellesen's avatar
Marcel Nellesen committed
300
301
                var projectRoleModel = new ProjectRoleModel();
                foreach (var projectRole in projectRoleModel.GetAllWhere((projectRole) => projectRole.ProjectId == project.Id))
302
303
304
                {
                    projectRoleModel.Delete(projectRole);
                }
305

Marcel Nellesen's avatar
Marcel Nellesen committed
306
307
                var projectDisciplineModel = new ProjectDisciplineModel();
                foreach (var projectDiscipline in projectDisciplineModel.GetAllWhere((projectDiscipline) => projectDiscipline.ProjectId == project.Id))
308
309
310
                {
                    projectDisciplineModel.Delete(projectDiscipline);
                }
311

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

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

Marcel Nellesen's avatar
Marcel Nellesen committed
324
325
                _activatedFeaturesModel.DeactivateAllFeatures(project);

326
327
328
329
330
331
332
                if (propegateAction)
                {
                    _emitter.EmitProjectDelete(new ProjectEventArgs(_configuration)
                    {
                        Project = project
                    });
                }
333

334
                _projectModel.HardDelete(project);
Marcel Nellesen's avatar
Marcel Nellesen committed
335
            }
336
337
338
339
            else
            {
                _projectModel.Delete(project);
            }
340
341
        }

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

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

359
            var project = _projectModel.StoreFromObject(projectObject, user, isRWTHMember);
360

361
            if (projectObject.ParentId != null
362
363
364
365
366
367
368
369
370
371
372
373
374
375
                && 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
            });

376
377
            LogAnalytics("Add Project", null, null, project.Id.ToString(), user);

378
            return Json(_projectModel.CreateReturnObjectFromDatabaseObject(project));
Benedikt Heinrichs's avatar
Benedikt Heinrichs committed
379
        }
380

381
382
383
384
385
        /// <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>
386
387
388
389
390
391
392
393
394
395
396
397
398
        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);
            }
399
            return new RdfStoreConnector(Program.Configuration.GetStringAndWait("coscine/local/virtuoso/additional/url")).GetTriples(new Uri("https://ror.org/04xfq0f34"), null, null, 1, externalIdList).Count() != 0;
400
401
        }

402
403
404
405
406
407
408
409
        /// <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>
410
411
412
413
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
        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
451
452
    }
}