commit 44e353e386ecd4a1a287b267bd1fa997798388e6 Author: Sergio Matias Urquin Date: Tue Apr 29 18:55:01 2025 -0600 Add project files. diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6733bfc --- /dev/null +++ b/.gitignore @@ -0,0 +1,352 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +CerberosBFFSettings.development.json +/Core.Cerberos.BFF.Api/cerberosprivkey.pem +/Core.Cerberos.BFF.Api/cerberospubkey.pem diff --git a/.pipelines/GitVersion.yml b/.pipelines/GitVersion.yml new file mode 100644 index 0000000..f5867a9 --- /dev/null +++ b/.pipelines/GitVersion.yml @@ -0,0 +1,59 @@ +mode: mainline +assembly-versioning-scheme: MajorMinorPatch +tag-prefix: '[vV]' +major-version-bump-message: '\+semver:\s?(breaking|major)' +minor-version-bump-message: '\+semver:\s?(feature|minor)' +patch-version-bump-message: '\+semver:\s?(fix|patch)' +no-bump-message: '\+semver:\s?(none|skip)' +legacy-semver-padding: 4 +build-metadata-padding: 4 +commits-since-version-source-padding: 4 +commit-message-incrementing: Enabled +branches: + main: + regex: ^master$|^main$ + tag: '' + increment: Patch + prevent-increment-of-merged-branch-version: true + track-merge-target: false + source-branches: [ 'develop', 'release' ] + tracks-release-branches: false + is-release-branch: false + is-mainline: true + pre-release-weight: 55000 + feature: + regex: ^features?[/-] + tag: useBranchName + increment: Inherit + prevent-increment-of-merged-branch-version: false + track-merge-target: false + source-branches: [ 'develop', 'main', 'release', 'feature', 'support', 'hotfix' ] + tracks-release-branches: false + is-release-branch: false + is-mainline: false + pre-release-weight: 30000 + release: + regex: ^releases?[/-] + tag: beta + increment: None + is-mainline: true + regex: ^releases?[/-] + prevent-increment-of-merged-branch-version: true + track-merge-target: false + source-branches: [ 'develop', 'main', 'support', 'release' ] + tracks-release-branches: false + is-release-branch: true + is-mainline: false + pre-release-weight: 30000 + hotfix: + regex: ^hotfix(es)?[/-] + increment: Patch + prevent-increment-of-merged-branch-version: false + track-merge-target: false + source-branches: [ 'develop', 'main', 'support' ] + tracks-release-branches: false + is-release-branch: false + is-mainline: false + pre-release-weight: 30000 +ignore: + sha: [] \ No newline at end of file diff --git a/.pipelines/templated-pipeline.yml b/.pipelines/templated-pipeline.yml new file mode 100644 index 0000000..7da5a2d --- /dev/null +++ b/.pipelines/templated-pipeline.yml @@ -0,0 +1,57 @@ +pool: + vmImage: 'windows-latest' + +trigger: + branches: + include: + - release/* + - feature/* + - hotfix/* + - bugfix/* + - development + +variables: + project: 'Core.Cerberos.BFF.Api/Core.Cerberos.BFF.Api.csproj' + solution: 'Core.Cerberos.BFF.sln' + buildConfiguration: 'Release' + artifactName: 'drop' + snykConnectionEndpoint: 'SnykConnection' + projectNameOnSonar: 'Core.Cerberos.BFF.Api' + projectKeyOnSonar: 'heathpbu_Core.Cerberos.BFF.Api' + feed: '1b3770f1-17db-4bf2-a43d-49f305aa7a22' + projectFileName: 'Core.Cerberos.BFF.Api.csproj' + projectPath: 'Core.Cerberos.BFF.Api/' + +resources: + repositories: + - repository: templates + name: "Template.DevOps.Pipelines" + type: "git" + project: "SharedLibs" + +jobs: +- job: CI + steps: + - template: templates/dotnet/v1/step1_setup.yml@templates + - template: templates/dotnet/v1/step2_versioning.yml@templates + parameters: + projectFileName: '$(projectFileName)' + path: '$(projectPath)' + - template: templates/dotnet/v1/step3_restore_and_build.yml@templates + parameters: + project: '$(project)' + solution: '$(solution)' + buildConfiguration: '$(buildConfiguration)' + projectNameOnSonar: '$(projectNameOnSonar)' + projectKeyOnSonar: '$(projectKeyOnSonar)' + feed: '$(feed)' + + - template: templates/dotnet/v1/step5_snyk_analysis.yml@templates + parameters: + snykConnectionEndpoint: '$(snykConnectionEndpoint)' + solutionToScan: '$(solution)' + + - template: templates/dotnet/v1/step6_release.yml@templates + parameters: + artifactName: '$(artifactName)' + project: '$(project)' \ No newline at end of file diff --git a/Core.Cerberos.BFF.Api/Controllers/AuthenticationController.cs b/Core.Cerberos.BFF.Api/Controllers/AuthenticationController.cs new file mode 100644 index 0000000..0bec018 --- /dev/null +++ b/Core.Cerberos.BFF.Api/Controllers/AuthenticationController.cs @@ -0,0 +1,107 @@ +using Asp.Versioning; +using Core.Cerberos.Adapters; +using Core.Cerberos.Adapters.Common.Constants; +using Core.Cerberos.Adapters.Contracts; +using Core.Cerberos.Application.UseCases.Users.Input; +using Core.Cerberos.External.Clients.Cerberos.Requests.Users; +using LSA.Dashboard.External.Clients.Dashboard; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Core.Cerberos.BFF.Api.Controllers +{ + /// + /// Handles all requests for Authentication. + /// + [ApiVersion(MimeTypes.ApplicationVersion)] + [Route("api/v{api-version:apiVersion}/[controller]")] + [Produces(MimeTypes.ApplicationJson)] + [Consumes(MimeTypes.ApplicationJson)] + [ApiController] + public class AuthenticationController(ICerberosServiceClient cerberosServiceClient, ILogger logger, ITokenService tokenService) : BaseController(logger) + { + /// + /// Get token for user. + /// + /// A representing + /// the asynchronous execution of the service. + /// The Token was generated. + /// The service internal error. + [HttpGet] + [Route(Routes.GenerateToken)] + [ProducesResponseType(typeof(UserAdapter), StatusCodes.Status200OK)] + [Authorize(AuthenticationSchemes = Schemes.AzureScheme)] + public async Task GenerateTokenService(CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(GenerateTokenService)} - Request received - Payload: {null}"); + + var tokenAdapter = new TokenAdapter(); + + var email = tokenService.GetEmailClaim(this.HttpContext); + + if (string.IsNullOrEmpty(email)) return BadRequest("An error ocurred while desearializing the token"); + + var tokenResult = await Handle(() => cerberosServiceClient.GetTokenAdapterService(new GetTokenAdapterRequest { Email = email }, cancellationToken)).ConfigureAwait(false); + + if (tokenResult is ObjectResult tokenOkResult && tokenOkResult.StatusCode == 200) + tokenAdapter = tokenOkResult.Value as TokenAdapter; + else + return tokenResult; + + if (tokenAdapter is not null && tokenAdapter.User is not null) + { + tokenAdapter.User.Token = tokenService.GenerateAccessToken(tokenAdapter); + + await Handle(() => cerberosServiceClient.LoginUserService(new LoginUserRequest { Email = email }, cancellationToken)).ConfigureAwait(false); + + return Ok(tokenAdapter.User); + } + else + { + return BadRequest("An error ocurred"); + } + } + + catch (Exception ex) + { + logger.LogError($"{nameof(GenerateTokenService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload : {null}"); + throw; + } + } + + /// + /// Refreshes the custom access token. + /// + /// A representing + /// the asynchronous execution of the service. + /// The user with it's new token. + /// The service internal error. + [HttpGet] + [Route(Routes.RefreshToken)] + [ProducesResponseType(typeof(UserAdapter), StatusCodes.Status200OK)] + [Authorize(AuthenticationSchemes = Schemes.HeathScheme)] + public async Task RefreshCustomTokenAsync(CancellationToken cancellationToken) + { + var tokenAdapter = new TokenAdapter(); + var email = tokenService.GetEmailClaim(this.HttpContext); + + var tokenResult = await Handle(() => cerberosServiceClient.GetTokenAdapterService(new GetTokenAdapterRequest { Email = email }, cancellationToken)).ConfigureAwait(false); + + if (tokenResult is ObjectResult tokenOkResult && tokenOkResult.StatusCode == 200) + { + tokenAdapter = tokenOkResult.Value as TokenAdapter; + + if (tokenAdapter != null) + { + var result = tokenService.RefreshAccessToken(HttpContext, tokenAdapter); + return result; + } + } + else return tokenResult; + + return new UnauthorizedObjectResult("Error in refreshToken"); + } + } +} diff --git a/Core.Cerberos.BFF.Api/Controllers/BaseController.cs b/Core.Cerberos.BFF.Api/Controllers/BaseController.cs new file mode 100644 index 0000000..b7b0912 --- /dev/null +++ b/Core.Cerberos.BFF.Api/Controllers/BaseController.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using Refit; + +namespace Core.Cerberos.BFF.Api.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class BaseController(ILogger logger) : ControllerBase + { + private readonly ILogger _logger = logger; + + protected Guid TrackingId => (Guid)(HttpContext.Items["TrackingId"] ?? Guid.NewGuid()); + + protected async Task Handle(Func>> apiCall) where T : class + { + var response = await apiCall().ConfigureAwait(false); + + _logger.LogInformation($"{TrackingId} - {response.RequestMessage?.Method} {response.RequestMessage?.RequestUri} - Status: {response.StatusCode}"); + + return FromAPIResponse(response); + } + + private IActionResult FromAPIResponse(ApiResponse response) where T : class + { + var errorContent = JsonConvert.DeserializeObject(response.Error?.Content ?? string.Empty) ?? string.Empty; + return StatusCode((int)response.StatusCode, (response.Content is not null) ? response.Content : errorContent); + } + } +} \ No newline at end of file diff --git a/Core.Cerberos.BFF.Api/Controllers/ModuleController.cs b/Core.Cerberos.BFF.Api/Controllers/ModuleController.cs new file mode 100644 index 0000000..13baf71 --- /dev/null +++ b/Core.Cerberos.BFF.Api/Controllers/ModuleController.cs @@ -0,0 +1,219 @@ +using Asp.Versioning; +using Core.Cerberos.Adapters; +using Core.Cerberos.Adapters.Attributes; +using Core.Cerberos.Adapters.Common.Constants; +using Core.Cerberos.External.Clients.Cerberos.Requests.Permissions; +using Lib.Architecture.BuildingBlocks; +using LSA.Dashboard.External.Clients.Dashboard; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Text.Json; + +namespace Core.Cerberos.BFF.Api.Controllers +{ + /// + /// Handles all requests for module authentication. + /// + [ApiVersion("1.0")] + [Route("api/v{version:apiVersion}/[controller]")] + [Consumes("application/json")] + [Produces("application/json")] + [ApiController] + [Authorize(AuthenticationSchemes = Schemes.HeathScheme)] + public class ModuleController(ICerberosServiceClient cerberosServiceClient, ILogger logger) : BaseController(logger) + { + /// + /// Gets all the modules. + /// + [HttpGet("GetAll")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("ModuleManagement.Read, RoleManagement.Read")] + public async Task GetAllModulesService(CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(GetAllModulesService)} - Request received - Payload: "); + + return await Handle(() => cerberosServiceClient.GetAllModulesService(new GetAllModulesRequest { }, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(GetAllModulesService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload"); + throw; + } + } + + /// + /// Gets all the modules by module identifiers. + /// + /// The request containing the list of module identifiers. + /// Cancellation token for the asynchronous operation. + /// The representing the result of the service call. + /// The modules found. + /// No content if no modules are found. + /// Bad request if the module identifiers are missing or invalid. + /// Unauthorized if the user is not authenticated. + /// Internal server error if an unexpected error occurs. + [HttpPost("GetAllByList")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("ModuleManagement.Read")] + public async Task GetAllModulesByListAsync([FromBody] GetAllModulesByListRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(GetAllModulesByListAsync)} - Request received - Payload: {request}"); + + if (request == null || request.Modules == null || !request.Modules.Any()) + { + return BadRequest("Module identifiers are required."); + } + + return await Handle(() => cerberosServiceClient.GetAllModulesByListService(request, cancellationToken)).ConfigureAwait(false); + + } + catch (Exception ex) + { + logger.LogError(ex, $"{nameof(GetAllModulesByListAsync)} - An error occurred - {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload: {request}"); + return StatusCode(StatusCodes.Status500InternalServerError, "Internal server error"); + } + } + + + /// + /// Creates a new module. + /// + [HttpPost("Create")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("ModuleManagement.Write")] + public async Task CreateModuleService(CreateModuleRequest newModule, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(CreateModuleService)} - Request received - Payload: {JsonSerializer.Serialize(newModule)}"); + + if (newModule == null) return BadRequest("Invalid module object"); + + if (string.IsNullOrEmpty(newModule.Name)) return BadRequest("Invalid module name"); + + if (string.IsNullOrEmpty(newModule.Description)) return BadRequest("Invalid module description"); + + if (string.IsNullOrEmpty(newModule.Route)) return BadRequest("Invalid module route"); + + return await Handle(() => cerberosServiceClient.CreateModuleService(newModule, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(CreateModuleService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(newModule)}"); + throw; + } + } + + /// + /// Gets the module by identifier. + /// + [HttpPost("GetById")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("ModuleManagement.Read")] + public async Task GetModuleByIdService(GetModuleRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(GetModuleByIdService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.Id)) return BadRequest("Invalid module identifier"); + + return await Handle(() => cerberosServiceClient.GetModuleByIdService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(GetModuleByIdService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + /// + /// Updates a full module by identifier. + /// + [HttpPut("Update")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("ModuleManagement.Write")] + public async Task UpdateModuleService(UpdateModuleRequest newModule, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(UpdateModuleService)} - Request received - Payload: {JsonSerializer.Serialize(newModule)}"); + + if (newModule == null) return BadRequest("Invalid module object"); + + if (string.IsNullOrEmpty(newModule.Name)) return BadRequest("Invalid module name"); + + if (string.IsNullOrEmpty(newModule.Description)) return BadRequest("Invalid module description"); + + if (string.IsNullOrEmpty(newModule.Route)) return BadRequest("Invalid module route"); + + return await Handle(() => cerberosServiceClient.UpdateModuleService(newModule, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(UpdateModuleService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(newModule)}"); + throw; + } + } + + /// + /// Changes the status of the module. + /// + [HttpPatch] + [Route("ChangeStatus")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("ModuleManagement.Write")] + public async Task ChangeModuleStatusService([FromBody] ChangeModuleStatusRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(ChangeModuleStatusService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.Id)) { return BadRequest("Invalid module identifier"); } + + return await Handle(() => cerberosServiceClient.ChangeModuleStatusService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(ChangeModuleStatusService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + + } +} diff --git a/Core.Cerberos.BFF.Api/Controllers/PermissionController.cs b/Core.Cerberos.BFF.Api/Controllers/PermissionController.cs new file mode 100644 index 0000000..43f80f5 --- /dev/null +++ b/Core.Cerberos.BFF.Api/Controllers/PermissionController.cs @@ -0,0 +1,216 @@ +using Asp.Versioning; +using Core.Cerberos.Adapters; +using Core.Cerberos.Adapters.Attributes; +using Core.Cerberos.Adapters.Common.Constants; +using Core.Cerberos.External.Clients.Cerberos.Requests.Permissions; +using Lib.Architecture.BuildingBlocks; +using LSA.Dashboard.External.Clients.Dashboard; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Graph; +using System.Text.Json; + +namespace Core.Cerberos.BFF.Api.Controllers +{ + /// + /// Handles all requests for permission authentication. + /// + [ApiVersion("1.0")] + [Route("api/v{version:apiVersion}/[controller]")] + [Consumes("application/json")] + [Produces("application/json")] + [ApiController] + [Authorize(AuthenticationSchemes = Schemes.HeathScheme)] + public class PermissionController(ICerberosServiceClient cerberosServiceClient, ILogger logger) : BaseController(logger) + { + /// + /// Gets all the permissions. + /// + [HttpGet("GetAll")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("PermissionManagement.Read, RoleManagement.Read")] + public async Task GetAllPermissionsService(CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(GetAllPermissionsService)} - Request received - Payload: "); + + return await Handle(() => cerberosServiceClient.GetAllPermissionsService(new GetAllPermissionsRequest { }, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(GetAllPermissionsService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload"); + throw; + } + } + + /// + /// Gets all the permissions by permission identifiers. + /// + /// The request containing the list of permission identifiers. + /// Cancellation token for the asynchronous operation. + /// The representing the result of the service call. + /// The permissions found. + /// No content if no permissions are found. + /// Bad request if the permission identifiers are missing or invalid. + /// Unauthorized if the user is not authenticated. + /// Internal server error if an unexpected error occurs. + [HttpPost("GetAllByList")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("PermissionManagement.Read")] + public async Task GetAllPermissionsByListAsync([FromBody] GetAllPermissionsByListRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(GetAllPermissionsByListAsync)} - Request received - Payload: {request}"); + + if (request == null || request.Permissions == null || !request.Permissions.Any()) + { + return BadRequest("Permission identifiers are required."); + } + + return await Handle(() => cerberosServiceClient.GetAllPermissionsByListService(request, cancellationToken)).ConfigureAwait(false); + + } + catch (Exception ex) + { + logger.LogError(ex, $"{nameof(GetAllPermissionsByListAsync)} - An error occurred - {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload: {request}"); + return StatusCode(StatusCodes.Status500InternalServerError, "Internal server error"); + } + } + + + /// + /// Creates a new permission. + /// + [HttpPost("Create")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("PermissionManagement.Write")] + public async Task CreatePermissionService(CreatePermissionRequest newPermission, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(CreatePermissionService)} - Request received - Payload: {JsonSerializer.Serialize(newPermission)}"); + + if (newPermission == null) return BadRequest("Invalid permission object"); + + if (string.IsNullOrEmpty(newPermission.Name)) return BadRequest("Invalid permission name"); + + if (string.IsNullOrEmpty(newPermission.Description)) return BadRequest("Invalid permission description"); + + return await Handle(() => cerberosServiceClient.CreatePermissionService(newPermission, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(CreatePermissionService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(newPermission)}"); + throw; + } + } + + /// + /// Gets the permission by identifier. + /// + [HttpPost("GetById")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("PermissionManagement.Read")] + public async Task GetPermissionByIdService(GetPermissionRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(GetPermissionByIdService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.Id)) return BadRequest("Invalid permission identifier"); + + return await Handle(() => cerberosServiceClient.GetPermissionByIdService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(GetPermissionByIdService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + /// + /// Updates a full permission by identifier. + /// + [HttpPut("Update")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("PermissionManagement.Write")] + public async Task UpdatePermissionService(UpdatePermissionRequest newPermission, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(UpdatePermissionService)} - Request received - Payload: {JsonSerializer.Serialize(newPermission)}"); + + if (newPermission == null) return BadRequest("Invalid permission object"); + + if (string.IsNullOrEmpty(newPermission.Name)) return BadRequest("Invalid permission name"); + + if (string.IsNullOrEmpty(newPermission.Description)) return BadRequest("Invalid permission description"); + + return await Handle(() => cerberosServiceClient.UpdatePermissionService(newPermission, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(UpdatePermissionService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(newPermission)}"); + throw; + } + } + + /// + /// Changes the status of the permission. + /// + [HttpPatch] + [Route("ChangeStatus")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("PermissionManagement.Write")] + public async Task ChangePermissionStatusService([FromBody] ChangePermissionStatusRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(ChangePermissionStatusService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.Id)) { return BadRequest("Invalid permission identifier"); } + + return await Handle(() => cerberosServiceClient.ChangePermissionStatusService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(ChangePermissionStatusService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + + } +} diff --git a/Core.Cerberos.BFF.Api/Controllers/RoleController.cs b/Core.Cerberos.BFF.Api/Controllers/RoleController.cs new file mode 100644 index 0000000..3088a1b --- /dev/null +++ b/Core.Cerberos.BFF.Api/Controllers/RoleController.cs @@ -0,0 +1,247 @@ +using Asp.Versioning; +using Core.Cerberos.Adapters.Attributes; +using Core.Cerberos.Adapters.Common.Constants; +using Core.Cerberos.Application.UseCases.Roles.Input; +using Lib.Architecture.BuildingBlocks; +using LSA.Dashboard.External.Clients.Dashboard; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Text.Json; + +namespace Core.Cerberos.BFF.Api.Controllers +{ + /// + /// Handles all requests for role authentication. + /// + [ApiVersion("1.0")] + [Route("api/v{version:apiVersion}/[controller]")] + [Consumes("application/json")] + [Produces("application/json")] + [ApiController] + [Authorize(AuthenticationSchemes = Schemes.HeathScheme)] + public class RoleController(ICerberosServiceClient cerberosServiceClient, ILogger logger) : BaseController(logger) + { + /// + /// Gets all the roles. + /// + [HttpGet("GetAll")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("RoleManagement.Read")] + public async Task GetAllRolesService(CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(GetAllRolesService)} - Request received - Payload: "); + + return await Handle(() => cerberosServiceClient.GetAllRolesService(new GetAllRolesRequest { }, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(GetAllRolesService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload"); + throw; + } + } + + /// + /// Creates a new role. + /// + [HttpPost("Create")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("RoleManagement.Write")] + public async Task CreateRoleService(CreateRoleRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(CreateRoleService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (request == null) return BadRequest("Invalid role object"); + + if (string.IsNullOrEmpty(request.Name)) return BadRequest("Invalid role name"); + + if (string.IsNullOrEmpty(request.Description)) return BadRequest("Invalid role description"); + + if (request.Applications?.Length <= 0) return BadRequest("Role must have at least one application"); + + if (request.Modules?.Length <= 0) return BadRequest("Role must have at least one module"); + + if (request.Permissions?.Length <= 0) return BadRequest("Role must have at least one permission"); + + return await Handle(() => cerberosServiceClient.CreateRoleService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(CreateRoleService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + /// + /// Gets the role by identifier. + /// + [HttpPost("GetById")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("RoleManagement.Read")] + public async Task GetRoleByIdService(GetRoleRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(GetRoleByIdService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.Id)) return BadRequest("Invalid role identifier"); + + return await Handle(() => cerberosServiceClient.GetRoleByIdService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(GetRoleByIdService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + /// + /// Updates a full role by identifier. + /// + [HttpPut("Update")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("RoleManagement.Write")] + public async Task UpdateRoleService(UpdateRoleRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(UpdateRoleService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (request == null) return BadRequest("Invalid role object"); + + if (string.IsNullOrEmpty(request.Name)) return BadRequest("Invalid role name"); + + if (string.IsNullOrEmpty(request.Id)) return BadRequest("Invalid role identifier"); + + if (string.IsNullOrEmpty(request.Description)) return BadRequest("Invalid role description"); + + if (request.Applications?.Length <= 0) return BadRequest("Role must have at least one application"); + + if (request.Modules?.Length <= 0) return BadRequest("Role must have at least one module"); + + if (request.Permissions?.Length <= 0) return BadRequest("Role must have at least one permission"); + + + return await Handle(() => cerberosServiceClient.UpdateRoleService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(UpdateRoleService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + /// + /// Changes the status of the role. + /// + [HttpPatch] + [Route("ChangeStatus")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("RoleManagement.Write")] + public async Task ChangeRoleStatusService([FromBody] ChangeRoleStatusRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(ChangeRoleStatusService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.Id)) { return BadRequest("Invalid role identifier"); } + + return await Handle(() => cerberosServiceClient.ChangeRoleStatusService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(ChangeRoleStatusService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + /// + /// Adds an application to the role's list of applications. + /// + [HttpPost] + [Route("AddApplication")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("RoleManagement.Write")] + public async Task AddApplicationToRoleService([FromBody] AddApplicationToRoleRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(AddApplicationToRoleService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.RoleId)) { return BadRequest("Invalid role identifier"); } + + return await Handle(() => cerberosServiceClient.AddApplicationToRoleService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(AddApplicationToRoleService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + /// + /// Removes an application from the role's list of applications. + /// + [HttpDelete] + [Route("RemoveApplication")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Permission("RoleManagement.Write")] + public async Task RemoveApplicationFromRoleService([FromBody] RemoveApplicationFromRoleRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(RemoveApplicationFromRoleService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.RoleId)) { return BadRequest("Invalid role identifier"); } + + return await Handle(() => cerberosServiceClient.RemoveApplicationFromRoleService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(RemoveApplicationFromRoleService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + } +} diff --git a/Core.Cerberos.BFF.Api/Controllers/UserController.cs b/Core.Cerberos.BFF.Api/Controllers/UserController.cs new file mode 100644 index 0000000..83b265e --- /dev/null +++ b/Core.Cerberos.BFF.Api/Controllers/UserController.cs @@ -0,0 +1,431 @@ +using Asp.Versioning; +using Core.Cerberos.Adapters.Attributes; +using Core.Cerberos.Adapters.Common.Constants; +using Core.Cerberos.Application.UseCases.Users.Input; +using Core.Cerberos.External.Clients.Cerberos.Requests.Users; +using Lib.Architecture.BuildingBlocks; +using LSA.Dashboard.External.Clients.Dashboard; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Text.Json; + +namespace Core.Cerberos.BFF.Api.Controllers +{ + /// + /// Handles all requests for user authentication. + /// + [ApiVersion("1.0")] + [Route("api/v{version:apiVersion}/[controller]")] + [Consumes("application/json")] + [Produces("application/json")] + [ApiController] + public class UserController(ICerberosServiceClient cerberosServiceClient, ILogger logger) : BaseController(logger) + { + /// + /// Gets all the users. + /// + [HttpGet("GetAll")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Authorize(AuthenticationSchemes = Schemes.HeathScheme)] + [Permission("UserManagement.Read")] + public async Task GetAllUsersService(CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(GetAllUsersService)} - Request received - Payload: "); + + return await Handle(() => cerberosServiceClient.GetAllUsersService(new GetAllUsersRequest { }, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(GetAllUsersService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload"); + throw; + } + } + + /// + /// Creates a new user. + /// + [HttpPost("Create")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Authorize(AuthenticationSchemes = Schemes.HeathScheme)] + [Permission("UserManagement.Write")] + public async Task CreateUserService(CreateUserRequest newUser, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(CreateUserService)} - Request received - Payload: {JsonSerializer.Serialize(newUser)}"); + + if (newUser == null) return BadRequest("Invalid user object"); + + if (string.IsNullOrEmpty(newUser.Email)) return BadRequest("Invalid user email"); + + if (string.IsNullOrEmpty(newUser.Name)) return BadRequest("Invalid user name"); + + if (string.IsNullOrEmpty(newUser.LastName)) return BadRequest("Invalid user lastname"); + + if (string.IsNullOrEmpty(newUser.RoleId)) return BadRequest("Invalid role id"); + + if (!newUser.Companies.Any()) return BadRequest("The user must contain at least one company"); + + return await Handle(() => cerberosServiceClient.CreateUserService(newUser, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(CreateUserService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(newUser)}"); + throw; + } + } + + /// + /// Gets the user by identifier. + /// + [HttpPost("GetById")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Authorize(AuthenticationSchemes = Schemes.HeathScheme)] + [Permission("UserManagement.Read")] + public async Task GetUserByIdService(GetUserRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(GetUserByIdService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.Id)) return BadRequest("Invalid user identifier"); + + return await Handle(() => cerberosServiceClient.GetUserByIdService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(GetUserByIdService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + /// + /// Gets the user by email. + /// + [HttpPost("GetByEmail")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Authorize(AuthenticationSchemes = Schemes.HeathScheme)] + [Permission("UserManagement.Read")] + public async Task GetUserByEmailService(GetUserByEmailRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(GetUserByEmailService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.Email)) return BadRequest("Invalid user email"); + + return await Handle(() => cerberosServiceClient.GetUserByEmailService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(GetUserByIdService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + /// + /// Updates a full user by identifier. + /// + [HttpPut("Update")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Authorize(AuthenticationSchemes = Schemes.HeathScheme)] + [Permission("UserManagement.Write")] + public async Task UpdateUserService(UpdateUserRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(UpdateUserService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (request == null) return BadRequest("Invalid user object"); + + if (string.IsNullOrEmpty(request.Email)) return BadRequest("Invalid user email"); + + if (string.IsNullOrEmpty(request.Name)) return BadRequest("Invalid user name"); + + if (string.IsNullOrEmpty(request.LastName)) return BadRequest("Invalid user lastname"); + + if (string.IsNullOrEmpty(request.RoleId)) return BadRequest("Invalid role id"); + + if (!request.Companies.Any()) return BadRequest("The user must contain at least one company"); + + return await Handle(() => cerberosServiceClient.UpdateUserService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(UpdateUserService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + /// + /// Logs in the user. + /// + [HttpPatch("LoginUser")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Authorize(AuthenticationSchemes = $"{Schemes.AzureScheme}, {Schemes.HeathScheme}")] + public async Task LoginUserService([FromBody] LoginUserRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(LoginUserService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.Email)) return BadRequest("Invalid user email"); + + return await Handle(() => cerberosServiceClient.LoginUserService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(LoginUserService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload"); + throw; + } + } + + /// + /// Logs out the user. + /// + [HttpPatch("LogoutUser")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Authorize(AuthenticationSchemes = $"{Schemes.AzureScheme}, {Schemes.HeathScheme}")] + public async Task LogoutUserService([FromBody] LogoutUserRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(LogoutUserService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.Email)) return BadRequest("Invalid user email"); + + return await Handle(() => cerberosServiceClient.LogoutUserService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(LogoutUserService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload"); + throw; + } + } + + /// + /// Changes the status of the user. + /// + [HttpPatch] + [Route("ChangeStatus")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Authorize(AuthenticationSchemes = Schemes.HeathScheme)] + [Permission("UserManagement.Write")] + public async Task ChangeUserStatusService([FromBody] ChangeUserStatusRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(ChangeUserStatusService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.Id)) { return BadRequest("Invalid user identifier"); } + + return await Handle(() => cerberosServiceClient.ChangeUserStatusService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(ChangeUserStatusService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + /// + /// Adds a company to the user's list of companies. + /// + [HttpPost] + [Route("AddCompany")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Authorize(AuthenticationSchemes = Schemes.HeathScheme)] + [Permission("UserManagement.Write")] + public async Task AddCompanyToUserService([FromBody] AddCompanyToUserRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(AddCompanyToUserService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.UserId)) { return BadRequest("Invalid user identifier"); } + if (string.IsNullOrEmpty(request.CompanyId)) { return BadRequest("Invalid company identifier"); } + + return await Handle(() => cerberosServiceClient.AddCompanyToUserService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(AddCompanyToUserService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + /// + /// Removes a company from the user's list of companies. + /// + [HttpDelete] + [Route("RemoveCompany")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Authorize(AuthenticationSchemes = Schemes.HeathScheme)] + [Permission("UserManagement.Write")] + public async Task RemoveCompanyFromUserService([FromBody] RemoveCompanyFromUserRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(RemoveCompanyFromUserService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.UserId)) { return BadRequest("Invalid user identifier"); } + if (string.IsNullOrEmpty(request.CompanyId)) { return BadRequest("Invalid company identifier"); } + + return await Handle(() => cerberosServiceClient.RemoveCompanyFromUserService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(RemoveCompanyFromUserService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + /// + /// Adds a project to the user's list of companies. + /// + [HttpPost] + [Route("AddProject")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Authorize(AuthenticationSchemes = Schemes.HeathScheme)] + [Permission("UserManagement.Write")] + + public async Task AddProjectToUserService([FromBody] AddProjectToUserRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(AddProjectToUserService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.UserId)) { return BadRequest("Invalid user identifier"); } + if (string.IsNullOrEmpty(request.ProjectId)) { return BadRequest("Invalid project identifier"); } + + return await Handle(() => cerberosServiceClient.AddProjectToUserService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(AddProjectToUserService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + /// + /// Removes a project from the user's list of companies. + /// + [HttpDelete] + [Route("RemoveProject")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Authorize(AuthenticationSchemes = Schemes.HeathScheme)] + [Permission("UserManagement.Write")] + public async Task RemoveProjectFromUserService([FromBody] RemoveProjectFromUserRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(RemoveProjectFromUserService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.UserId)) { return BadRequest("Invalid user identifier"); } + if (string.IsNullOrEmpty(request.ProjectId)) { return BadRequest("Invalid project identifier"); } + + return await Handle(() => cerberosServiceClient.RemoveProjectFromUserService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(RemoveProjectFromUserService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + + /// + /// Gets the user by email. + /// + [HttpPost("ValidateExistence")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)] + [ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [AllowAnonymous] + public async Task ValidateUserExistenceService(ValidateUserExistenceRequest request, CancellationToken cancellationToken) + { + try + { + logger.LogInformation($"{nameof(ValidateUserExistenceService)} - Request received - Payload: {JsonSerializer.Serialize(request)}"); + + if (string.IsNullOrEmpty(request.Email)) return BadRequest("Invalid user email"); + + return await Handle(() => cerberosServiceClient.ValidateUserExistenceService(request, cancellationToken)).ConfigureAwait(false); + } + catch (Exception ex) + { + logger.LogError($"{nameof(GetUserByIdService)} - An Error Occurred- {ex.Message} - {ex.InnerException} - {ex.StackTrace} - with payload {JsonSerializer.Serialize(request)}"); + throw; + } + } + } +} diff --git a/Core.Cerberos.BFF.Api/Core.Cerberos.BFF.Api.csproj b/Core.Cerberos.BFF.Api/Core.Cerberos.BFF.Api.csproj new file mode 100644 index 0000000..9bef2d4 --- /dev/null +++ b/Core.Cerberos.BFF.Api/Core.Cerberos.BFF.Api.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + enable + enable + True + + + + + + + + + + + + + PreserveNewest + true + PreserveNewest + + + + diff --git a/Core.Cerberos.BFF.Api/Core.Cerberos.BFF.Api.http b/Core.Cerberos.BFF.Api/Core.Cerberos.BFF.Api.http new file mode 100644 index 0000000..903dc24 --- /dev/null +++ b/Core.Cerberos.BFF.Api/Core.Cerberos.BFF.Api.http @@ -0,0 +1,6 @@ +@Core.Cerberos.BFF.Api_HostAddress = http://localhost:5219 + +GET {{Core.Cerberos.BFF.Api_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/Core.Cerberos.BFF.Api/Program.cs b/Core.Cerberos.BFF.Api/Program.cs new file mode 100644 index 0000000..1d57e7d --- /dev/null +++ b/Core.Cerberos.BFF.Api/Program.cs @@ -0,0 +1,123 @@ +using Core.Cerberos.Adapters.Extensions; +using Core.Cerberos.Adapters.Helpers; +using Core.Cerberos.External.ClientConfiguration; +using Microsoft.AspNetCore.ResponseCompression; +using OpenTelemetry.Logs; +using OpenTelemetry.Resources; +using System.IO.Compression; +using System.Reflection; + +var builder = WebApplication.CreateBuilder(args); + +var authSettings = AuthHelper.GetAuthSettings(builder, "cerberos_bff"); + +builder.Services.ConfigureAuthentication(builder.Configuration, authSettings); + +builder.Services.AddEndpointsApiExplorer(); +builder.Configuration + .AddUserSecrets(Assembly.GetExecutingAssembly()) + .AddEnvironmentVariables(); + +builder.Services.AddResponseCompression(); +builder.Services.AddProblemDetails(); +builder.Services.AddMemoryCache(); +builder.Services.AddResponseCaching(configureOptions => { configureOptions.UseCaseSensitivePaths = true; }); +builder.Logging.AddOpenTelemetry(options => +{ + options.IncludeScopes = true; + options.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("core.cerberos.bff.api")).AddConsoleExporter(); +}); + +builder.Host.ConfigureServices((context, services) => +{ + builder.Services.AddHsts(options => + { + options.Preload = true; + options.IncludeSubDomains = true; + options.MaxAge = TimeSpan.FromDays(60); + }); + builder.Services.AddResponseCaching(configureOptions => + { + configureOptions.UseCaseSensitivePaths = true; + configureOptions.MaximumBodySize = 2048; + }); + builder.Services.AddHttpsRedirection(options => + { + options.RedirectStatusCode = 308; + }); + + services.AddHttpLogging(http => + { + http.CombineLogs = true; + }); + services.AddAntiforgery(); + + services.AddCors(options => + { + options.AddPolicy("AllowAll", policyBuilder => + policyBuilder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()); + }); + services.AddMvc().AddJsonOptions(options => + { + options.JsonSerializerOptions.WriteIndented = true; + options.JsonSerializerOptions.MaxDepth = 20; + options.JsonSerializerOptions.NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowNamedFloatingPointLiterals; + }); + services.Configure(options => + { + options.Level = CompressionLevel.SmallestSize; + }); + services.Configure(options => + { + options.Level = CompressionLevel.SmallestSize; + }); + services.AddResponseCompression(options => + { + options.EnableForHttps = true; + options.Providers.Add(); + options.Providers.Add(); + }); + services.AddResponseCaching(); + services.AddControllers(); + services.AddEndpointsApiExplorer(); + services.AddVersioning(builder.Configuration); + services.AddSwagger(builder.Configuration, "Core.Cerberos.BFF.API.xml", authSettings); + services.AddLogging(); + services.AddProblemDetails(); + services.AddHttpContextAccessor(); + services.AddTransient(); // Register the TrackingIdHandler + services.RegisterExternalLayer(builder.Configuration); + services.AddTelemetry(); +}); + +builder.Services.AddCors(options => +{ + options.AddDefaultPolicy( + builder => + { + builder.AllowAnyOrigin() + .AllowAnyHeader() + .AllowAnyMethod(); + }); +}); + +//*************************************************************************// +var app = builder.Build(); + + +app.UseCors(options => options.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin()); +app.UseSwagger(); +app.UseSwaggerUI(builder.Configuration, authSettings); +app.ConfigureSwagger(builder.Configuration); +app.UseResponseCompression(); +app.UseResponseCaching(); +app.UseHttpsRedirection(); + +app.UseAuthentication(); +app.UseAuthorization(); +app.MapControllers(); +app.UseHsts(); +app.UseAntiforgery(); +app.UseHttpLogging(); + +app.Run(); \ No newline at end of file diff --git a/Core.Cerberos.BFF.Api/Properties/launchSettings.json b/Core.Cerberos.BFF.Api/Properties/launchSettings.json new file mode 100644 index 0000000..bfcac2b --- /dev/null +++ b/Core.Cerberos.BFF.Api/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:47596", + "sslPort": 44320 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5219", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Local" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7239;http://localhost:5219", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Local" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Local" + } + } + } +} diff --git a/Core.Cerberos.BFF.Api/appsettings.Development.json b/Core.Cerberos.BFF.Api/appsettings.Development.json new file mode 100644 index 0000000..21b22db --- /dev/null +++ b/Core.Cerberos.BFF.Api/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} + diff --git a/Core.Cerberos.BFF.Api/appsettings.Local.json b/Core.Cerberos.BFF.Api/appsettings.Local.json new file mode 100644 index 0000000..2a049fe --- /dev/null +++ b/Core.Cerberos.BFF.Api/appsettings.Local.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "LocalGateways": { + "CerberosService": "https://localhost:7253/api" + } +} + diff --git a/Core.Cerberos.BFF.Api/appsettings.json b/Core.Cerberos.BFF.Api/appsettings.json new file mode 100644 index 0000000..d321977 --- /dev/null +++ b/Core.Cerberos.BFF.Api/appsettings.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "Endpoints": { + "AppConfigurationURI": "https://sandbox-hci-usc-appcg.azconfig.io" + } +} diff --git a/Core.Cerberos.BFF.Api/sample.settings.json b/Core.Cerberos.BFF.Api/sample.settings.json new file mode 100644 index 0000000..7aafb9b --- /dev/null +++ b/Core.Cerberos.BFF.Api/sample.settings.json @@ -0,0 +1,31 @@ +{ + "Gateways": { + "CerberosService": "" // Service endpoint for Cerberos + }, + "ConnectionStrings": { + "KeyVault": "" //KeyVault Uri + }, + "JwtIssuerOptions": { + "Audience": "", // Audience for token creation, specifies intended recipients + "Issuer": "" // Issuer for token creation, identifies the issuer of the token + }, + "AzureAdB2C": { + "Instance": "", // Azure AD instance URL (STORED IN KEY VAULT) + "TenantId": "", // Azure AD tenant ID (STORED IN KEY VAULT) + "ClientId": "", // Azure AD application client ID (STORED IN KEY VAULT) + "ClientSecret": "", // Azure AD application client secret (STORED IN KEY VAULT) + "CallbackPath": "", // Path for redirect after authentication + "Scopes": "" // Access scopes for user permissions + }, + "HeathCerberosApp": { + "AuthorizationUrl": "", // URL for authorization endpoint(STORED IN KEY VAULT) + "TokenUrl": "", // URL for token endpoint(STORED IN KEY VAULT) + "Scope": "", // Scope for application permissions (STORED IN KEY VAULT) + "ClientId": "" // Client ID for Kerberos application (STORED IN KEY VAULT) + }, + "MicrosoftGraph": { + "Scopes": "", // Scopes for Microsoft Graph API access + "BaseUrl": "" // Base URL for Microsoft Graph API + } +} + diff --git a/Core.Cerberos.BFF.sln b/Core.Cerberos.BFF.sln new file mode 100644 index 0000000..45420d2 --- /dev/null +++ b/Core.Cerberos.BFF.sln @@ -0,0 +1,36 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35027.167 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Cerberos.BFF.Api", "Core.Cerberos.BFF.Api\Core.Cerberos.BFF.Api.csproj", "{3272E8EA-B46B-49DF-9D29-B5BE758E87C4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Cerberos.External", "Core.Cerberos.External\Core.Cerberos.External.csproj", "{3111115C-A391-4A4C-A886-016A92BAC20E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Application", "Application", "{CBFF88ED-B50D-4A71-BB2A-C0B2BBA489CA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3272E8EA-B46B-49DF-9D29-B5BE758E87C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3272E8EA-B46B-49DF-9D29-B5BE758E87C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3272E8EA-B46B-49DF-9D29-B5BE758E87C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3272E8EA-B46B-49DF-9D29-B5BE758E87C4}.Release|Any CPU.Build.0 = Release|Any CPU + {3111115C-A391-4A4C-A886-016A92BAC20E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3111115C-A391-4A4C-A886-016A92BAC20E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3111115C-A391-4A4C-A886-016A92BAC20E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3111115C-A391-4A4C-A886-016A92BAC20E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3111115C-A391-4A4C-A886-016A92BAC20E} = {CBFF88ED-B50D-4A71-BB2A-C0B2BBA489CA} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {887E5371-E4E7-4821-8AAB-CDE7F4BFF2CD} + EndGlobalSection +EndGlobal diff --git a/Core.Cerberos.External/ClientConfiguration/RegisterClientConfiguration.cs b/Core.Cerberos.External/ClientConfiguration/RegisterClientConfiguration.cs new file mode 100644 index 0000000..4acf949 --- /dev/null +++ b/Core.Cerberos.External/ClientConfiguration/RegisterClientConfiguration.cs @@ -0,0 +1,67 @@ +using Core.Cerberos.Adapters.Contracts; +using Core.Cerberos.Adapters.Handlers; +using Core.Cerberos.Adapters.TokenProvider; +using Core.Cerberos.External.GatewayConfigurations; +using LSA.Dashboard.External.Clients.Dashboard; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Refit; + +namespace Core.Cerberos.External.ClientConfiguration +{ + public static class RegisterClientConfiguration + { + public static IServiceCollection RegisterExternalLayer(this IServiceCollection services, IConfiguration configuration) + { + var gatewayConfiguration = new GatewayConfiguration(); + var gatewaySettingsConfiguration = new GatewaySettingsConfigurations(configuration); + + // Register GatewayConfiguration as a singleton + services.AddSingleton(gatewayConfiguration); + + // Register IHttpContextAccessor + services.AddSingleton(); + + // Register ITokenProvider + services.AddSingleton(); + + // Register the custom AuthenticatedHttpClientHandler + services.AddTransient(provider => + { + var tokenProvider = provider.GetRequiredService(); + var trackingIdHandler = new TrackingMechanismExtension(provider.GetRequiredService()); + + var handler = new AuthenticatedHttpClientHandler(tokenProvider) + { + InnerHandler = new HttpClientHandler() // Setting the InnerHandler manually + }; + + // Attach the TrackingIdHandler as the outermost handler + trackingIdHandler.InnerHandler = handler; + + return handler; + }); + + var cerberosServiceApiUrl = GatewaySettingsConfigurations.GetCerberosServiceAPIEndpoint().Endpoint.Url; + + // Register IDashBoardServiceClient with the manually created HttpClient + services.AddScoped(provider => + { + var handler = provider.GetRequiredService(); + var handlerTrackingId = new TrackingMechanismExtension(provider.GetRequiredService()); // Using the TrackingIdHandler here + // Chain the handlers + handlerTrackingId.InnerHandler = handler; //chaining + + var httpClient = new HttpClient(handlerTrackingId) + { + BaseAddress = new Uri(cerberosServiceApiUrl), + Timeout = TimeSpan.FromMinutes(1) + }; + return RestService.For(httpClient); + }); + + return services; + } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/ICerberosServiceClient.cs b/Core.Cerberos.External/Clients/Cerberos/ICerberosServiceClient.cs new file mode 100644 index 0000000..43dbf7c --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/ICerberosServiceClient.cs @@ -0,0 +1,118 @@ +using Core.Cerberos.Adapters; +using Core.Cerberos.Application.UseCases.Roles.Input; +using Core.Cerberos.Application.UseCases.Users.Input; +using Core.Cerberos.External.Clients.Cerberos.Requests.Permissions; +using Core.Cerberos.External.Clients.Cerberos.Requests.Users; +using Microsoft.AspNetCore.Mvc; +using Refit; + +namespace LSA.Dashboard.External.Clients.Dashboard +{ + public interface ICerberosServiceClient + { + [Post("/v1/User/Create")] + Task> CreateUserService([Header("TrackingId")][Body] CreateUserRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/User/GetById")] + Task> GetUserByIdService([Header("TrackingId")][Body] GetUserRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/User/GetByEmail")] + Task> GetUserByEmailService([Header("TrackingId")][Body] GetUserByEmailRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/User/ValidateExistence")] + Task> ValidateUserExistenceService([Header("TrackingId")][Body] ValidateUserExistenceRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/User/GetByEmail")] + Task> GetUserByEmailService([Header("Authorization")] string? authorization, [Header("TrackingId")][Body] GetUserByEmailRequest request, CancellationToken cancellationToken = default); + + [Get("/v1/User/GetAll")] + Task>> GetAllUsersService([Header("TrackingId")][Body] GetAllUsersRequest request, CancellationToken cancellationToken = default); + + [Put("/v1/User/Update")] + Task> UpdateUserService([Header("TrackingId")][Body] UpdateUserRequest request, CancellationToken cancellationToken = default); + + [Patch("/v1/User/LoginUser")] + Task> LoginUserService([Header("TrackingId")][Body] LoginUserRequest request, CancellationToken cancellationToken = default); + + [Patch("/v1/User/LogoutUser")] + Task> LogoutUserService([Header("TrackingId")][FromBody] LogoutUserRequest request, CancellationToken cancellationToken = default); + + [Patch("/v1/User/ChangeStatus")] + Task> ChangeUserStatusService([Header("TrackingId")][Body] ChangeUserStatusRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/User/AddCompany")] + Task> AddCompanyToUserService([Header("TrackingId")][Body] AddCompanyToUserRequest request, CancellationToken cancellationToken = default); + + [Delete("/v1/User/RemoveCompany")] + Task> RemoveCompanyFromUserService([Header("TrackingId")][Body] RemoveCompanyFromUserRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/User/AddProject")] + Task> AddProjectToUserService([Header("TrackingId")][Body] AddProjectToUserRequest request, CancellationToken cancellationToken = default); + + [Delete("/v1/User/RemoveProject")] + Task> RemoveProjectFromUserService([Header("TrackingId")][Body] RemoveProjectFromUserRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/User/GetTokenAdapter")] + Task> GetTokenAdapterService([Header("TrackingId")][Body] GetTokenAdapterRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/Role/Create")] + Task> CreateRoleService([Header("TrackingId")][Body] CreateRoleRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/Role/GetById")] + Task> GetRoleByIdService([Header("TrackingId")][Body] GetRoleRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/Role/GetById")] + Task> GetRoleByIdService([Header("Authorization")] string? authorization, [Header("TrackingId")][Body] GetRoleRequest request, CancellationToken cancellationToken = default); + + [Get("/v1/Role/GetAll")] + Task>> GetAllRolesService([Header("TrackingId")][Body] GetAllRolesRequest request, CancellationToken cancellationToken = default); + + [Put("/v1/Role/Update")] + Task> UpdateRoleService([Header("TrackingId")][Body] UpdateRoleRequest request, CancellationToken cancellationToken = default); + + [Patch("/v1/Role/ChangeStatus")] + Task> ChangeRoleStatusService([Header("TrackingId")][Body] ChangeRoleStatusRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/Role/AddApplication")] + Task> AddApplicationToRoleService([Header("TrackingId")][Body] AddApplicationToRoleRequest request, CancellationToken cancellationToken = default); + + [Delete("/v1/Role/RemoveApplication")] + Task> RemoveApplicationFromRoleService([Header("TrackingId")][Body] RemoveApplicationFromRoleRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/Permission/Create")] + Task> CreatePermissionService([Header("TrackingId")][Body] CreatePermissionRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/Permission/GetById")] + Task> GetPermissionByIdService([Header("TrackingId")][Body] GetPermissionRequest request, CancellationToken cancellationToken = default); + + [Get("/v1/Permission/GetAll")] + Task>> GetAllPermissionsService([Header("TrackingId")][Body] GetAllPermissionsRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/Permission/GetPermissionList")] + Task>> GetAllPermissionsByListService([Header("TrackingId")][Body] GetAllPermissionsByListRequest request, CancellationToken cancellationToken = default); + + [Put("/v1/Permission/Update")] + Task> UpdatePermissionService([Header("TrackingId")][Body] UpdatePermissionRequest request, CancellationToken cancellationToken = default); + + [Patch("/v1/Permission/ChangeStatus")] + Task> ChangePermissionStatusService([Header("TrackingId")][Body] ChangePermissionStatusRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/Module/Create")] + Task> CreateModuleService([Header("TrackingId")][Body] CreateModuleRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/Module/GetById")] + Task> GetModuleByIdService([Header("TrackingId")][Body] GetModuleRequest request, CancellationToken cancellationToken = default); + + [Get("/v1/Module/GetAll")] + Task>> GetAllModulesService([Header("TrackingId")][Body] GetAllModulesRequest request, CancellationToken cancellationToken = default); + + [Post("/v1/Module/GetModuleList")] + Task>> GetAllModulesByListService([Header("TrackingId")][Body] GetAllModulesByListRequest request, CancellationToken cancellationToken = default); + + [Put("/v1/Module/Update")] + Task> UpdateModuleService([Header("TrackingId")][Body] UpdateModuleRequest request, CancellationToken cancellationToken = default); + + [Patch("/v1/Module/ChangeStatus")] + Task> ChangeModuleStatusService([Header("TrackingId")][Body] ChangeModuleStatusRequest request, CancellationToken cancellationToken = default); + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/ChangeModuleStatusRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/ChangeModuleStatusRequest.cs new file mode 100644 index 0000000..b3cfa44 --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/ChangeModuleStatusRequest.cs @@ -0,0 +1,10 @@ +using Core.Cerberos.Adapters.Common.Enums; + +namespace Core.Cerberos.External.Clients.Cerberos.Requests.Permissions +{ + public class ChangeModuleStatusRequest + { + public string Id { get; set; } + public StatusEnum Status { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/CreateModuleRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/CreateModuleRequest.cs new file mode 100644 index 0000000..ecf7192 --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/CreateModuleRequest.cs @@ -0,0 +1,14 @@ +using Core.Cerberos.Adapters.Common.Enums; + +namespace Core.Cerberos.External.Clients.Cerberos.Requests.Permissions +{ + public class CreateModuleRequest + { + public string Name { get; set; } = null!; + public string? Description { get; set; } + public string? Icon { get; set; } + public string Route { get; set; } = null!; + public int? Order { get; set; } + public ApplicationsEnum? Application { get; set; } = null!; + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/GetAllModulesByListRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/GetAllModulesByListRequest.cs new file mode 100644 index 0000000..b52d89a --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/GetAllModulesByListRequest.cs @@ -0,0 +1,7 @@ +namespace Core.Cerberos.External.Clients.Cerberos.Requests.Permissions +{ + public class GetAllModulesByListRequest + { + public string[] Modules { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/GetAllModulesRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/GetAllModulesRequest.cs new file mode 100644 index 0000000..dffae3e --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/GetAllModulesRequest.cs @@ -0,0 +1,6 @@ +namespace Core.Cerberos.External.Clients.Cerberos.Requests.Permissions +{ + public class GetAllModulesRequest + { + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/GetModuleRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/GetModuleRequest.cs new file mode 100644 index 0000000..09c29e0 --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/GetModuleRequest.cs @@ -0,0 +1,7 @@ +namespace Core.Cerberos.External.Clients.Cerberos.Requests.Permissions +{ + public class GetModuleRequest + { + public string Id { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/UpdateModuleRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/UpdateModuleRequest.cs new file mode 100644 index 0000000..f137728 --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Modules/UpdateModuleRequest.cs @@ -0,0 +1,17 @@ +using Core.Cerberos.Adapters.Common.Enums; + +namespace Core.Cerberos.External.Clients.Cerberos.Requests.Permissions +{ + public class UpdateModuleRequest + { + public string Id { get; set; } = null!; + public string Name { get; set; } = null!; + public string? Description { get; set; } + public string? Icon { get; set; } + public string Route { get; set; } = null!; + public int? Order { get; set; } + + public ApplicationsEnum? Application { get; set; } = null!; + public StatusEnum Status { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/ChangePermissionStatusRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/ChangePermissionStatusRequest.cs new file mode 100644 index 0000000..afeeebd --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/ChangePermissionStatusRequest.cs @@ -0,0 +1,10 @@ +using Core.Cerberos.Adapters.Common.Enums; + +namespace Core.Cerberos.External.Clients.Cerberos.Requests.Permissions +{ + public class ChangePermissionStatusRequest + { + public string Id { get; set; } + public StatusEnum Status { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/CreatePermissionRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/CreatePermissionRequest.cs new file mode 100644 index 0000000..7b3fd3b --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/CreatePermissionRequest.cs @@ -0,0 +1,11 @@ +using Core.Cerberos.Adapters.Common.Constants; + +namespace Core.Cerberos.External.Clients.Cerberos.Requests.Permissions +{ + public class CreatePermissionRequest + { + public string Name { get; set; } = null!; + public string Description { get; set; } = null!; + public AccessLevelEnum? AccessLevel { get; set; } = null!; + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/GetAllPermissionsByListRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/GetAllPermissionsByListRequest.cs new file mode 100644 index 0000000..236da5a --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/GetAllPermissionsByListRequest.cs @@ -0,0 +1,7 @@ +namespace Core.Cerberos.External.Clients.Cerberos.Requests.Permissions +{ + public class GetAllPermissionsByListRequest + { + public string[] Permissions { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/GetAllPermissionsRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/GetAllPermissionsRequest.cs new file mode 100644 index 0000000..779abb5 --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/GetAllPermissionsRequest.cs @@ -0,0 +1,6 @@ +namespace Core.Cerberos.External.Clients.Cerberos.Requests.Permissions +{ + public class GetAllPermissionsRequest + { + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/GetPermissionRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/GetPermissionRequest.cs new file mode 100644 index 0000000..4e168ca --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/GetPermissionRequest.cs @@ -0,0 +1,7 @@ +namespace Core.Cerberos.External.Clients.Cerberos.Requests.Permissions +{ + public class GetPermissionRequest + { + public string Id { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/UpdatePermissionRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/UpdatePermissionRequest.cs new file mode 100644 index 0000000..c8159bc --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Permissions/UpdatePermissionRequest.cs @@ -0,0 +1,14 @@ +using Core.Cerberos.Adapters.Common.Constants; +using Core.Cerberos.Adapters.Common.Enums; + +namespace Core.Cerberos.External.Clients.Cerberos.Requests.Permissions +{ + public class UpdatePermissionRequest + { + public string Id { get; set; } = null!; + public string Name { get; set; } = null!; + public string? Description { get; set; } + public AccessLevelEnum? AccessLevel { get; set; } = null!; + public StatusEnum Status { get; set; } = StatusEnum.Active; + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/AddApplicationToRoleRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/AddApplicationToRoleRequest.cs new file mode 100644 index 0000000..67563ee --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/AddApplicationToRoleRequest.cs @@ -0,0 +1,10 @@ +using Core.Cerberos.Adapters.Common.Enums; + +namespace Core.Cerberos.Application.UseCases.Roles.Input +{ + public class AddApplicationToRoleRequest + { + public string RoleId { get; set; } + public ApplicationsEnum Application { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/ChangeRoleStatusRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/ChangeRoleStatusRequest.cs new file mode 100644 index 0000000..4116c0a --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/ChangeRoleStatusRequest.cs @@ -0,0 +1,11 @@ +using Core.Cerberos.Adapters.Common.Enums; + +namespace Core.Cerberos.Application.UseCases.Roles.Input +{ + public class ChangeRoleStatusRequest + { + public string Id { get; set; } + public StatusEnum Status { get; set; } + + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/CreateRoleRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/CreateRoleRequest.cs new file mode 100644 index 0000000..83d232d --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/CreateRoleRequest.cs @@ -0,0 +1,15 @@ +using Core.Cerberos.Adapters.Common.Enums; +using System.Text.Json.Serialization; + +namespace Core.Cerberos.Application.UseCases.Roles.Input +{ + public class CreateRoleRequest + { + public string Name { get; set; } = null!; + public string Description { get; set; } = null!; + [JsonConverter(typeof(EnumArrayJsonConverter))] + public ApplicationsEnum[]? Applications { get; set; } = null!; + public string[] Modules { get; set; } = null!; + public string[] Permissions { get; set; } = null!; + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/GetAllRolesRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/GetAllRolesRequest.cs new file mode 100644 index 0000000..2cd9f2b --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/GetAllRolesRequest.cs @@ -0,0 +1,6 @@ +namespace Core.Cerberos.Application.UseCases.Roles.Input +{ + public class GetAllRolesRequest + { + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/GetRoleRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/GetRoleRequest.cs new file mode 100644 index 0000000..d68f886 --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/GetRoleRequest.cs @@ -0,0 +1,7 @@ +namespace Core.Cerberos.Application.UseCases.Roles.Input +{ + public class GetRoleRequest + { + public string Id { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/RemoveApplicationFromRoleRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/RemoveApplicationFromRoleRequest.cs new file mode 100644 index 0000000..fb2ad92 --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/RemoveApplicationFromRoleRequest.cs @@ -0,0 +1,10 @@ +using Core.Cerberos.Adapters.Common.Enums; + +namespace Core.Cerberos.Application.UseCases.Roles.Input +{ + public class RemoveApplicationFromRoleRequest + { + public string RoleId { get; set; } + public ApplicationsEnum Application { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/UpdateRoleRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/UpdateRoleRequest.cs new file mode 100644 index 0000000..af6f028 --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Roles/UpdateRoleRequest.cs @@ -0,0 +1,17 @@ +using Core.Cerberos.Adapters.Common.Enums; +using System.Text.Json.Serialization; + +namespace Core.Cerberos.Application.UseCases.Roles.Input +{ + public class UpdateRoleRequest + { + public string Id { get; set; } = null!; + public string Name { get; set; } = null!; + public string? Description { get; set; } + [JsonConverter(typeof(EnumArrayJsonConverter))] + public ApplicationsEnum[]? Applications { get; set; } + public string[] Modules { get; set; } = null!; + public string[] Permissions { get; set; } = null!; + public StatusEnum Status { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Users/AddCompanyToUserRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/AddCompanyToUserRequest.cs new file mode 100644 index 0000000..250ce1c --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/AddCompanyToUserRequest.cs @@ -0,0 +1,8 @@ +namespace Core.Cerberos.Application.UseCases.Users.Input +{ + public class AddCompanyToUserRequest + { + public string UserId { get; set; } + public string CompanyId { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Users/AddProjectToUserRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/AddProjectToUserRequest.cs new file mode 100644 index 0000000..9c9819f --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/AddProjectToUserRequest.cs @@ -0,0 +1,8 @@ +namespace Core.Cerberos.Application.UseCases.Users.Input +{ + public class AddProjectToUserRequest + { + public string UserId { get; set; } + public string ProjectId { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Users/ChangeUserStatusRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/ChangeUserStatusRequest.cs new file mode 100644 index 0000000..23d1d4f --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/ChangeUserStatusRequest.cs @@ -0,0 +1,10 @@ +using Core.Cerberos.Adapters.Common.Enums; + +namespace Core.Cerberos.Application.UseCases.Users.Input +{ + public class ChangeUserStatusRequest + { + public string Id { get; set; } + public StatusEnum Status { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Users/CreateUserRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/CreateUserRequest.cs new file mode 100644 index 0000000..a15767c --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/CreateUserRequest.cs @@ -0,0 +1,14 @@ +namespace Core.Cerberos.Application.UseCases.Users.Input +{ + public class CreateUserRequest + { + public string Email { get; set; } = null!; + public string Name { get; set; } = null!; + public string? MiddleName { get; set; } + public string LastName { get; set; } = null!; + public string RoleId { get; set; } = null!; + public string[] Companies { get; set; } = null!; + public string[]? Projects { get; set; } + public bool SendInvitation { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Users/GetAllUsersRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/GetAllUsersRequest.cs new file mode 100644 index 0000000..ae7657f --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/GetAllUsersRequest.cs @@ -0,0 +1,6 @@ +namespace Core.Cerberos.Application.UseCases.Users.Input +{ + public class GetAllUsersRequest + { + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Users/GetEmailRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/GetEmailRequest.cs new file mode 100644 index 0000000..6e24476 --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/GetEmailRequest.cs @@ -0,0 +1,7 @@ +namespace Core.Cerberos.Application.UseCases.Users.Input +{ + public class GetUserByEmailRequest + { + public string Email { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Users/GetTokenAdapterRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/GetTokenAdapterRequest.cs new file mode 100644 index 0000000..bcbc033 --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/GetTokenAdapterRequest.cs @@ -0,0 +1,7 @@ +namespace Core.Cerberos.Application.UseCases.Users.Input +{ + public class GetTokenAdapterRequest + { + public string Email { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Users/GetUserRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/GetUserRequest.cs new file mode 100644 index 0000000..4dd1b01 --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/GetUserRequest.cs @@ -0,0 +1,7 @@ +namespace Core.Cerberos.Application.UseCases.Users.Input +{ + public class GetUserRequest + { + public string Id { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Users/LoginUserRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/LoginUserRequest.cs new file mode 100644 index 0000000..633735c --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/LoginUserRequest.cs @@ -0,0 +1,7 @@ +namespace Core.Cerberos.External.Clients.Cerberos.Requests.Users +{ + public class LoginUserRequest + { + public string Email { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Users/LogoutUserRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/LogoutUserRequest.cs new file mode 100644 index 0000000..39c8f25 --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/LogoutUserRequest.cs @@ -0,0 +1,7 @@ +namespace Core.Cerberos.External.Clients.Cerberos.Requests.Users +{ + public class LogoutUserRequest + { + public string Email { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Users/RemoveCompanyFromUserRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/RemoveCompanyFromUserRequest.cs new file mode 100644 index 0000000..526e4a8 --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/RemoveCompanyFromUserRequest.cs @@ -0,0 +1,8 @@ +namespace Core.Cerberos.Application.UseCases.Users.Input +{ + public class RemoveCompanyFromUserRequest + { + public string UserId { get; set; } + public string CompanyId { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Users/RemoveProjectFromUserRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/RemoveProjectFromUserRequest.cs new file mode 100644 index 0000000..a81c402 --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/RemoveProjectFromUserRequest.cs @@ -0,0 +1,8 @@ +namespace Core.Cerberos.Application.UseCases.Users.Input +{ + public class RemoveProjectFromUserRequest + { + public string UserId { get; set; } + public string ProjectId { get; set; } + } +} diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Users/UpdateUserRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/UpdateUserRequest.cs new file mode 100644 index 0000000..a5851af --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/UpdateUserRequest.cs @@ -0,0 +1,17 @@ +using Core.Cerberos.Adapters.Common.Enums; + +namespace Core.Cerberos.Application.UseCases.Users.Input +{ + public class UpdateUserRequest + { + public string Id { get; set; } = null!; + public string Email { get; set; } = null!; + public string Name { get; set; } = null!; + public string? MiddleName { get; set; } + public string LastName { get; set; } = null!; + public string RoleId { get; set; } = null!; + public string[] Companies { get; set; } = null!; + public string[]? Projects { get; set; } + public StatusEnum Status { get; set; } + } +} \ No newline at end of file diff --git a/Core.Cerberos.External/Clients/Cerberos/Requests/Users/ValidateUserExistenceRequest.cs b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/ValidateUserExistenceRequest.cs new file mode 100644 index 0000000..4dba8b5 --- /dev/null +++ b/Core.Cerberos.External/Clients/Cerberos/Requests/Users/ValidateUserExistenceRequest.cs @@ -0,0 +1,7 @@ +namespace Core.Cerberos.Application.UseCases.Users.Input +{ + public class ValidateUserExistenceRequest + { + public string Email { get; set; } + } +} diff --git a/Core.Cerberos.External/Core.Cerberos.External.csproj b/Core.Cerberos.External/Core.Cerberos.External.csproj new file mode 100644 index 0000000..387eeac --- /dev/null +++ b/Core.Cerberos.External/Core.Cerberos.External.csproj @@ -0,0 +1,15 @@ + + + + net8.0 + enable + enable + + + + + + + + + diff --git a/Core.Cerberos.External/GatewayConfigurations/GatewayConfiguration.cs b/Core.Cerberos.External/GatewayConfigurations/GatewayConfiguration.cs new file mode 100644 index 0000000..3ff78d2 --- /dev/null +++ b/Core.Cerberos.External/GatewayConfigurations/GatewayConfiguration.cs @@ -0,0 +1,19 @@ +using Core.Blueprint.External; + +namespace Core.Cerberos.External.GatewayConfigurations +{ + public record GatewayConfiguration + { + public GatewayConfiguration() + { + CerberosService = new CerberosServiceAPI(); + } + + public CerberosServiceAPI CerberosService { get; set; } + } + public record CerberosServiceAPI + { + public string Channel { get; set; } + public BaseEndpoint Endpoint { get; set; } + } +} diff --git a/Core.Cerberos.External/GatewayConfigurations/GatewaySettingsConfigurations.cs b/Core.Cerberos.External/GatewayConfigurations/GatewaySettingsConfigurations.cs new file mode 100644 index 0000000..cd4dc49 --- /dev/null +++ b/Core.Cerberos.External/GatewayConfigurations/GatewaySettingsConfigurations.cs @@ -0,0 +1,48 @@ +using Core.Blueprint.External; +using Microsoft.Extensions.Configuration; + +namespace Core.Cerberos.External.GatewayConfigurations +{ + public class GatewaySettingsConfigurations + { + private static GatewayConfiguration GatewayConfigurations { get; set; } = new GatewayConfiguration(); + private readonly IConfiguration _configuration; + + public GatewaySettingsConfigurations(IConfiguration configuration) + { + _configuration = configuration; + SetDashboardServiceAPIEndpoint(); + } + public static CerberosServiceAPI GetCerberosServiceAPIEndpoint() + { + return GatewayConfigurations.CerberosService; + } + private GatewayConfiguration SetDashboardServiceAPIEndpoint() + { + IConfigurationSection source; + var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? string.Empty; + + if (environment == "Local") + source = _configuration.GetSection("LocalGateways"); + else + source = _configuration.GetSection("Gateways"); + + var endpoint = source["CerberosService"] ?? string.Empty; + + if (string.IsNullOrEmpty(endpoint)) throw new Exception("Cerberos Service endpoint is empty or null"); + + GatewayConfigurations.CerberosService = new CerberosServiceAPI() + { + Endpoint = new BaseEndpoint() + { + Uri = new Uri(endpoint), + Url = endpoint, + Token = string.Empty, + APIName = "Cerberos Service" + } + }; + + return GatewayConfigurations; + } + } +} diff --git a/Core.Cerberos.External/TrackingMechanismExtension.cs b/Core.Cerberos.External/TrackingMechanismExtension.cs new file mode 100644 index 0000000..09940ca --- /dev/null +++ b/Core.Cerberos.External/TrackingMechanismExtension.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Http; + +namespace Core.Cerberos.External +{ + public sealed class TrackingMechanismExtension(IHttpContextAccessor httpContextAccessor) : DelegatingHandler + { + private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + if (_httpContextAccessor.HttpContext != null) + { + request.Headers.Add("TrackingId", Guid.NewGuid().ToString()); + } + return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); + } + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..e37e4b1 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# Introduction +TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project. + +# Getting Started +TODO: Guide users through getting your code up and running on their own system. In this section you can talk about: +1. Installation process +2. Software dependencies +3. Latest releases +4. API references + +# Build and Test +TODO: Describe and show how to build your code and run the tests. + +# Contribute +TODO: Explain how other users and developers can contribute to make your code better. + +If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files: +- [ASP.NET Core](https://github.com/aspnet/Home) +- [Visual Studio Code](https://github.com/Microsoft/vscode) +- [Chakra Core](https://github.com/Microsoft/ChakraCore) \ No newline at end of file