diff --git a/LearningHub.Nhs.WebUI/Interfaces/IReportService.cs b/LearningHub.Nhs.WebUI/Interfaces/IReportService.cs new file mode 100644 index 00000000..2b2260f2 --- /dev/null +++ b/LearningHub.Nhs.WebUI/Interfaces/IReportService.cs @@ -0,0 +1,19 @@ +namespace LearningHub.Nhs.WebUI.Interfaces +{ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + using elfhHub.Nhs.Models.Common; + + /// + /// Defines the . + /// + public interface IReportService + { + /// + /// The GetReporterPermission. + /// + /// A representing the result of the asynchronous operation. + Task GetReporterPermission(); + } +} diff --git a/LearningHub.Nhs.WebUI/Models/NavigationModel.cs b/LearningHub.Nhs.WebUI/Models/NavigationModel.cs index 25b51671..93456496 100644 --- a/LearningHub.Nhs.WebUI/Models/NavigationModel.cs +++ b/LearningHub.Nhs.WebUI/Models/NavigationModel.cs @@ -75,6 +75,11 @@ public class NavigationModel /// public bool ShowBrowseCatalogues { get; set; } + /// + /// Gets or sets a value indicating whether to show reports. + /// + public bool ShowReports { get; set; } + /// /// Gets or sets a value indicating whether ShowHome. /// diff --git a/LearningHub.Nhs.WebUI/Services/NavigationPermissionService.cs b/LearningHub.Nhs.WebUI/Services/NavigationPermissionService.cs index 7c357ada..bae61e8e 100644 --- a/LearningHub.Nhs.WebUI/Services/NavigationPermissionService.cs +++ b/LearningHub.Nhs.WebUI/Services/NavigationPermissionService.cs @@ -12,18 +12,22 @@ public class NavigationPermissionService : INavigationPermissionService { private readonly IResourceService resourceService; private readonly IUserGroupService userGroupService; + private readonly IReportService reportService; /// /// Initializes a new instance of the class. /// /// Resource service. /// UserGroup service. + /// Report Service. public NavigationPermissionService( IResourceService resourceService, - IUserGroupService userGroupService) + IUserGroupService userGroupService, + IReportService reportService) { this.resourceService = resourceService; this.userGroupService = userGroupService; + this.reportService = reportService; } /// @@ -87,6 +91,7 @@ public NavigationModel NotAuthenticated() ShowSignOut = false, ShowMyAccount = false, ShowBrowseCatalogues = false, + ShowReports = false, }; } @@ -113,6 +118,7 @@ private NavigationModel AuthenticatedAdministrator(string controllerName) ShowSignOut = true, ShowMyAccount = true, ShowBrowseCatalogues = true, + ShowReports = true, }; } @@ -139,6 +145,7 @@ private async Task AuthenticatedBlueUser(string controllerName) ShowSignOut = true, ShowMyAccount = true, ShowBrowseCatalogues = true, + ShowReports = await this.reportService.GetReporterPermission(), }; } @@ -164,6 +171,7 @@ private NavigationModel AuthenticatedGuest() ShowSignOut = true, ShowMyAccount = false, ShowBrowseCatalogues = false, + ShowReports = false, }; } @@ -190,6 +198,7 @@ private async Task AuthenticatedReadOnly(string controllerName) ShowSignOut = true, ShowMyAccount = false, ShowBrowseCatalogues = true, + ShowReports = await this.reportService.GetReporterPermission(), }; } @@ -215,6 +224,7 @@ private async Task AuthenticatedBasicUserOnly() ShowSignOut = true, ShowMyAccount = true, ShowBrowseCatalogues = true, + ShowReports = false, }; } @@ -240,6 +250,7 @@ private NavigationModel InLoginWizard() ShowSignOut = true, ShowMyAccount = false, ShowBrowseCatalogues = false, + ShowReports = false, }; } } diff --git a/LearningHub.Nhs.WebUI/Services/ReportService.cs b/LearningHub.Nhs.WebUI/Services/ReportService.cs new file mode 100644 index 00000000..3e94492b --- /dev/null +++ b/LearningHub.Nhs.WebUI/Services/ReportService.cs @@ -0,0 +1,55 @@ +namespace LearningHub.Nhs.WebUI.Services +{ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + using elfhHub.Nhs.Models.Common; + using LearningHub.Nhs.WebUI.Interfaces; + using Microsoft.Extensions.Logging; + using Newtonsoft.Json; + + /// + /// Defines the . + /// + public class ReportService : BaseService, IReportService + { + /// + /// Initializes a new instance of the class. + /// + /// The Web Api Http Client. + /// The Open Api Http Client. + /// logger. + public ReportService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger) + : base(learningHubHttpClient, openApiHttpClient, logger) + { + } + + /// + /// The GetAllAsync. + /// + /// The . + public async Task GetReporterPermission() + { + bool viewmodel = false; + + var client = await this.OpenApiHttpClient.GetClientAsync(); + + var request = $"Report/GetReporterPermission"; + var response = await client.GetAsync(request).ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + var result = response.Content.ReadAsStringAsync().Result; + viewmodel = JsonConvert.DeserializeObject(result); + } + else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized + || + response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + throw new Exception("AccessDenied"); + } + + return viewmodel; + } + } +} diff --git a/LearningHub.Nhs.WebUI/Startup/ServiceMappings.cs b/LearningHub.Nhs.WebUI/Startup/ServiceMappings.cs index 9fd65bd6..e919efca 100644 --- a/LearningHub.Nhs.WebUI/Startup/ServiceMappings.cs +++ b/LearningHub.Nhs.WebUI/Startup/ServiceMappings.cs @@ -81,6 +81,7 @@ public static void AddLearningHubMappings(this IServiceCollection services, ICon services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/DatabricksConfig.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/DatabricksConfig.cs new file mode 100644 index 00000000..4973b74f --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/DatabricksConfig.cs @@ -0,0 +1,48 @@ +namespace LearningHub.Nhs.OpenApi.Models.Configuration +{ + /// + /// DatabricksConfig + /// + public class DatabricksConfig + { + /// + /// Gets or sets the ResourceId for the databricks instance. + /// + public string ResourceId { get; set; } = null!; + + /// + /// Gets or sets the base url for the databricks instance. + /// + public string InstanceUrl { get; set; } = null!; + + /// + /// Gets or sets the warehouse id for databricks. + /// + public string WarehouseId { get; set; } = null!; + + /// + /// Gets or sets the tenant Id of the service pricncipl. + /// + public string TenantId { get; set; } = null!; + + /// + /// Gets or sets the client Id of the service pricncipl. + /// + public string ClientId { get; set; } = null!; + + /// + /// Gets or sets the client scret of the service pricncipl. + /// + public string clientSecret { get; set; } = null!; + + /// + /// Gets or sets the endpoint to check user permission. + /// + public string UserPermissionEndpoint { get; set; } = "databricks_poc2_ws_uks.tel_unified_reporting_gold.sp_isreporter"; //null!; + + /// + /// Gets or sets the token. + /// + public string Token { get; set; } = ""; + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/LearningHubConfig.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/LearningHubConfig.cs index 81b91aa6..d1552d42 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/LearningHubConfig.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/LearningHubConfig.cs @@ -142,6 +142,10 @@ public class LearningHubConfig /// public string BrowseCataloguesUrl { get; set; } = null!; + /// + /// Gets or sets . + /// + public string ReportUrl { get; set; } = null!; /// diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/NavigationModel.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/NavigationModel.cs index 912d99ca..c7ccc786 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/NavigationModel.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/NavigationModel.cs @@ -75,6 +75,11 @@ public class NavigationModel /// public bool ShowBrowseCatalogues { get; set; } + /// + /// Gets or sets a value indicating whether to show reports. + /// + public bool ShowReports { get; set; } + /// /// Gets or sets a value indicating whether to show home. /// diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/HttpClients/IDatabricksApiHttpClient.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/HttpClients/IDatabricksApiHttpClient.cs new file mode 100644 index 00000000..f4226cd8 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/HttpClients/IDatabricksApiHttpClient.cs @@ -0,0 +1,26 @@ +namespace LearningHub.Nhs.OpenApi.Services.Interface.HttpClients +{ + using System; + using System.Net.Http; + using System.Threading.Tasks; + + /// + /// The Bookmark Http Client interface. + /// + public interface IDatabricksApiHttpClient : IDisposable + { + /// + /// GETs data from Databricks API. + /// + /// The URL to make a get call to. + /// Optional authorization header. + /// A representing the result of the asynchronous operation. + Task GetData(string requestUrl, string? authHeader); + + /// + /// The Get Client method. + /// + /// The . + HttpClient GetClient(); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IDatabricksService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IDatabricksService.cs new file mode 100644 index 00000000..494b5f54 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IDatabricksService.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; + +namespace LearningHub.Nhs.OpenApi.Services.Interface.Services +{ + /// + /// IDatabricks service + /// + public interface IDatabricksService + { + /// + /// IsUserReporter. + /// + /// The userId. + /// A representing the result of the asynchronous operation. + Task IsUserReporter(int userId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/HttpClients/DatabricksApiHttpClient.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services/HttpClients/DatabricksApiHttpClient.cs new file mode 100644 index 00000000..b5452240 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/HttpClients/DatabricksApiHttpClient.cs @@ -0,0 +1,63 @@ +namespace LearningHub.Nhs.OpenApi.Services.HttpClients +{ + using System; + using System.Net.Http; + using System.Net.Http.Headers; + using System.Threading.Tasks; + using IdentityModel.Client; + using LearningHub.Nhs.OpenApi.Models.Configuration; + using LearningHub.Nhs.OpenApi.Services.Interface.HttpClients; + using Microsoft.Extensions.Options; + using Microsoft.IdentityModel.Protocols.OpenIdConnect; + + /// + /// Http client for Databricks. + /// + public class DatabricksApiHttpClient : IDatabricksApiHttpClient + { + private readonly HttpClient httpClient; + private readonly IOptions databricksConfig; + + /// + /// Initializes a new instance of the class. + /// + /// Configuration details for the databricks. + public DatabricksApiHttpClient(IOptions databricksConfig) + { + this.databricksConfig = databricksConfig; + this.httpClient = new HttpClient { BaseAddress = new Uri(databricksConfig.Value.InstanceUrl) }; + this.httpClient.DefaultRequestHeaders.Accept.Clear(); + this.httpClient.DefaultRequestHeaders.Accept.Add( + new MediaTypeWithQualityHeaderValue("application/json")); + } + + /// + public void Dispose() + { + this.httpClient.Dispose(); + } + + /// + /// The Get Client method. + /// + /// The . + public HttpClient GetClient() + { + string accessToken = this.databricksConfig.Value.Token; + this.httpClient.SetBearerToken(accessToken); + return this.httpClient; + } + + /// + public async Task GetData(string requestUrl, string? authHeader) + { + if (!string.IsNullOrEmpty(authHeader)) + { + this.httpClient.SetBearerToken(authHeader); + } + + var message = await this.httpClient.GetAsync(requestUrl).ConfigureAwait(false); + return message; + } + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/DatabricksService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/DatabricksService.cs new file mode 100644 index 00000000..898e85b5 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/DatabricksService.cs @@ -0,0 +1,71 @@ +using LearningHub.Nhs.Models.Bookmark; +using LearningHub.Nhs.Models.Entities.Reporting; +using LearningHub.Nhs.OpenApi.Models.Configuration; +using LearningHub.Nhs.OpenApi.Services.HttpClients; +using LearningHub.Nhs.OpenApi.Services.Interface.HttpClients; +using LearningHub.Nhs.OpenApi.Services.Interface.Services; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Text.Json.Nodes; +using System.Text; +using System.Threading.Tasks; + +namespace LearningHub.Nhs.OpenApi.Services.Services +{ + /// + /// DatabricksService + /// + public class DatabricksService : IDatabricksService + { + private readonly IOptions databricksConfig; + + /// + /// Initializes a new instance of the class. + /// + /// databricksConfig. + public DatabricksService(IOptions databricksConfig) + { + this.databricksConfig = databricksConfig; + } + + /// + public async Task IsUserReporter(int userId) + { + DatabricksApiHttpClient databricksInstance = new DatabricksApiHttpClient(this.databricksConfig); + + var sqlText = $"CALL {this.databricksConfig.Value.UserPermissionEndpoint}({userId});"; + const string requestUrl = "/api/2.0/sql/statements"; + + var requestPayload = new + { + warehouse_id = this.databricksConfig.Value.WarehouseId, + statement = sqlText, + wait_timeout = "60s", + on_wait_timeout = "CANCEL" + }; + + var jsonBody = JsonConvert.SerializeObject(requestPayload); + using var content = new StringContent(jsonBody, Encoding.UTF8, "application/json"); + + var response = await databricksInstance.GetClient().PostAsync(requestUrl, content); + + var databricksResponse = await databricksInstance.GetClient().PostAsync(requestUrl, content); + if (databricksResponse.StatusCode is not HttpStatusCode.OK) + { + //log failure + return false; + } + var responseResult = await databricksResponse.Content.ReadAsStringAsync(); + + responseResult = responseResult.Trim(); + + return JsonConvert.DeserializeObject(responseResult); + } + } + +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/NavigationPermissionService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/NavigationPermissionService.cs index bd4cb5e1..8f02a796 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/NavigationPermissionService.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/NavigationPermissionService.cs @@ -13,16 +13,19 @@ public class NavigationPermissionService : INavigationPermissionService { private readonly IResourceService resourceService; private readonly IUserGroupService userGroupService; + private readonly IDatabricksService databricksService; /// /// Initializes a new instance of the class. /// /// Resource service. /// userGroup service. - public NavigationPermissionService(IResourceService resourceService, IUserGroupService userGroupService) + /// databricksService. + public NavigationPermissionService(IResourceService resourceService, IUserGroupService userGroupService, IDatabricksService databricksService) { this.resourceService = resourceService; this.userGroupService = userGroupService; + this.databricksService = databricksService; } /// @@ -85,6 +88,7 @@ public NavigationModel NotAuthenticated() ShowSignOut = false, ShowMyAccount = false, ShowBrowseCatalogues = false, + ShowReports = false, }; } @@ -110,6 +114,7 @@ private NavigationModel AuthenticatedAdministrator(string controllerName) ShowSignOut = true, ShowMyAccount = true, ShowBrowseCatalogues = true, + ShowReports = true, }; } @@ -136,6 +141,7 @@ private async Task AuthenticatedBlueUser(string controllerName, ShowSignOut = true, ShowMyAccount = true, ShowBrowseCatalogues = true, + ShowReports = await this.databricksService.IsUserReporter(userId), }; } @@ -160,6 +166,7 @@ private NavigationModel AuthenticatedGuest() ShowSignOut = true, ShowMyAccount = false, ShowBrowseCatalogues = false, + ShowReports = false, }; } @@ -185,6 +192,7 @@ private async Task AuthenticatedReadOnly(string controllerName, ShowSignOut = true, ShowMyAccount = false, ShowBrowseCatalogues = true, + ShowReports = await this.databricksService.IsUserReporter(userId), }; } @@ -209,6 +217,7 @@ private async Task AuthenticatedBasicUserOnly(int userId) ShowSignOut = true, ShowMyAccount = true, ShowBrowseCatalogues = true, + ShowReports = false, }; } @@ -233,6 +242,7 @@ private NavigationModel InLoginWizard() ShowSignOut = true, ShowMyAccount = false, ShowBrowseCatalogues = false, + ShowReports = false, }; } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Startup.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Startup.cs index f00f1325..6730c3aa 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Startup.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Startup.cs @@ -24,6 +24,7 @@ public static void AddServices(this IServiceCollection services) { services.AddScoped(); services.AddHttpClient(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); @@ -35,6 +36,7 @@ public static void AddServices(this IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/Configuration/ConfigurationExtensions.cs b/OpenAPI/LearningHub.Nhs.OpenApi/Configuration/ConfigurationExtensions.cs index 7d5a6179..1c5a692e 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi/Configuration/ConfigurationExtensions.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi/Configuration/ConfigurationExtensions.cs @@ -45,6 +45,11 @@ public static class ConfigurationExtensions /// public const string MoodleSectionName = "Moodle"; + /// + /// The DatabricksSectionName. + /// + public const string DatabricksSectionName = "Databricks"; + /// /// Adds config. /// @@ -65,6 +70,8 @@ public static void AddConfig(this IServiceCollection services, IConfiguration co services.AddOptions().Bind(config.GetSection(AzureSectionName)); services.AddOptions().Bind(config.GetSection(MoodleSectionName)); + + services.AddOptions().Bind(config.GetSection(DatabricksSectionName)); } private static OptionsBuilder RegisterPostConfigure(this OptionsBuilder builder) diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/ReportController.cs b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/ReportController.cs new file mode 100644 index 00000000..989e29fb --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/ReportController.cs @@ -0,0 +1,37 @@ +namespace LearningHub.NHS.OpenAPI.Controllers +{ + using System.Threading.Tasks; + using LearningHub.Nhs.OpenApi.Services.Interface.Services; + using Microsoft.AspNetCore.Authorization; + using Microsoft.AspNetCore.Mvc; + + /// + /// Report Controller. + /// + [ApiController] + [Authorize] + public class ReportController : OpenApiControllerBase + { + private readonly IDatabricksService databricksService; + + /// + /// Initializes a new instance of the class. + /// + /// The catalogue service. + public ReportController(IDatabricksService databricksService) + { + this.databricksService = databricksService; + } + + /// + /// Get all catalogues. + /// + /// Task. + [HttpGet] + [Route("GetReporterPermission")] + public async Task GetReporterPermission() + { + return await this.databricksService.IsUserReporter(this.CurrentUserId.GetValueOrDefault()); + } + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/UserController.cs b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/UserController.cs index dafb3b58..66213725 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/UserController.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/UserController.cs @@ -353,7 +353,6 @@ public async Task>> GetLHUserNavigation() return this.MenuItems(model); } - private List> MenuItems(NavigationModel model) { var menu = new List> @@ -382,6 +381,12 @@ private List> MenuItems(NavigationModel model) { "url", this.learningHubConfig.BrowseCataloguesUrl }, { "visible", model.ShowBrowseCatalogues }, }, + new Dictionary + { + { "title", "Reports" }, + { "url", this.learningHubConfig.ReportUrl }, + { "visible", model.ShowReports }, + }, new Dictionary { { "title", "Admin" }, diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/LearningHub.NHS.OpenAPI.csproj.user b/OpenAPI/LearningHub.Nhs.OpenApi/LearningHub.NHS.OpenAPI.csproj.user index 5bdd1216..1d8db97a 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi/LearningHub.NHS.OpenAPI.csproj.user +++ b/OpenAPI/LearningHub.Nhs.OpenApi/LearningHub.NHS.OpenAPI.csproj.user @@ -5,7 +5,7 @@ IIS Local - ApiControllerEmptyScaffolder - root/Common/Api + MvcControllerEmptyScaffolder + root/Common/MVC/Controller \ No newline at end of file diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/appsettings.json b/OpenAPI/LearningHub.Nhs.OpenApi/appsettings.json index bec2f230..c9f768ba 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi/appsettings.json +++ b/OpenAPI/LearningHub.Nhs.OpenApi/appsettings.json @@ -107,7 +107,8 @@ "RegisterUrl": "/register", "SignOutUrl": "/home/logout", "MyAccountUrl": "/myaccount", - "BrowseCataloguesUrl": "/allcatalogue" + "BrowseCataloguesUrl": "/allcatalogue", + "ReportUrl": "" }, "LearningHubAPIConfig": { "BaseUrl": "https://learninghub.nhs.uk/api"