// Copyright Epic Games, Inc. All Rights Reserved. using HordeServer.Api; using HordeServer.Collections; using HordeServer.Models; using HordeServer.Services; using HordeServer.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using MongoDB.Bson; using MongoDB.Driver; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace HordeServer.Controllers { using UserId = ObjectId; /// /// Controller for the /api/v1/agents endpoint /// [ApiController] [Authorize] [Route("[controller]")] public class SubscriptionsController : ControllerBase { /// /// The ACL service singleton /// AclService AclService; /// /// Collection of subscription documents /// ISubscriptionCollection SubscriptionCollection; /// /// Constructor /// /// The acl service singleton /// The collection of subscription documents public SubscriptionsController(AclService AclService, ISubscriptionCollection SubscriptionCollection) { this.AclService = AclService; this.SubscriptionCollection = SubscriptionCollection; } /// /// Find subscriptions matching a criteria /// /// Name of the user /// Filter for properties to return /// List of subscriptions [HttpGet] [Route("/api/v1/subscriptions")] [ProducesResponseType(typeof(List), 200)] public async Task>> GetSubscriptionsAsync([FromQuery] string UserId, [FromQuery] PropertyFilter? Filter = null) { UserId UserIdValue; if (!TryParseUserId(UserId, out UserIdValue)) { return BadRequest("Invalid user id"); } if (!await AclService.AuthorizeAsUserAsync(User, UserIdValue)) { return Forbid(); } List Results = await SubscriptionCollection.FindSubscriptionsAsync(UserIdValue); return Results.ConvertAll(x => PropertyFilter.Apply(new GetSubscriptionResponse(x), Filter)); } /// /// Find subscriptions matching a criteria /// /// The subscription id /// Filter for properties to return /// List of subscriptions [HttpGet] [Route("/api/v1/subscriptions/{SubscriptionId}")] [ProducesResponseType(typeof(GetSubscriptionResponse), 200)] public async Task> GetSubscriptionAsync(string SubscriptionId, [FromQuery] PropertyFilter? Filter = null) { ISubscription? Subscription = await SubscriptionCollection.GetAsync(SubscriptionId); if (Subscription == null) { return NotFound(); } if (!await AclService.AuthorizeAsUserAsync(User, Subscription.UserId)) { return Forbid(); } return PropertyFilter.Apply(new GetSubscriptionResponse(Subscription), Filter); } /// /// Remove a subscription /// /// The subscription id /// Async task [HttpDelete] [Route("/api/v1/subscriptions/{SubscriptionId}")] [ProducesResponseType(typeof(List), 200)] public async Task DeleteSubscriptionAsync(string SubscriptionId) { ISubscription? Subscription = await SubscriptionCollection.GetAsync(SubscriptionId); if (Subscription == null) { return NotFound(); } if (!await AclService.AuthorizeAsUserAsync(User, Subscription.UserId)) { return Forbid(); } await SubscriptionCollection.RemoveAsync(new[] { Subscription }); return Ok(); } /// /// Find subscriptions matching a criteria /// /// The new subscriptions to create /// List of subscriptions [HttpPost] [Route("/api/v1/subscriptions")] public async Task>> CreateSubscriptionsAsync(List Subscriptions) { HashSet AuthorizedUsers = new HashSet(); UserId? CurrentUserId = User.GetUserId(); if(CurrentUserId != null) { AuthorizedUsers.Add(CurrentUserId.Value); } GlobalPermissionsCache Cache = new GlobalPermissionsCache(); List NewSubscriptions = new List(); foreach (CreateSubscriptionRequest Subscription in Subscriptions) { UserId NewUserId; if (!TryParseUserId(Subscription.UserId, out NewUserId)) { return BadRequest($"Invalid user id: '{Subscription.UserId}'."); } if (AuthorizedUsers.Add(NewUserId) && !await AclService.AuthorizeAsync(AclAction.Impersonate, User, Cache)) { return Forbid(); } NewSubscriptions.Add(new NewSubscription(Subscription.Event, NewUserId, Subscription.NotificationType)); } List Results = await SubscriptionCollection.AddAsync(NewSubscriptions); return Results.ConvertAll(x => new CreateSubscriptionResponse(x)); } /// /// Parse a user id from a string. Allows passing the user's name as well as their objectid value. /// /// /// /// bool TryParseUserId(string UserName, out UserId ObjectId) { UserId NewObjectId; if (UserId.TryParse(UserName, out NewObjectId)) { ObjectId = NewObjectId; return true; } string? CurrentUserName = User.GetUserName(); if (CurrentUserName != null && String.Equals(UserName, CurrentUserName, StringComparison.OrdinalIgnoreCase)) { UserId? CurrentUserId = User.GetUserId(); if (CurrentUserId != null) { ObjectId = CurrentUserId.Value; return true; } } ObjectId = default; return false; } } }