diff --git a/src/CryptoAgent/Controllers/UserKeysController.cs b/src/CryptoAgent/Controllers/UserKeysController.cs index 4cca2f8..877d956 100644 --- a/src/CryptoAgent/Controllers/UserKeysController.cs +++ b/src/CryptoAgent/Controllers/UserKeysController.cs @@ -1,33 +1,42 @@ using Bit.CryptoAgent.Models; using Bit.CryptoAgent.Repositories; using Bit.CryptoAgent.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using System; +using System.Security.Claims; using System.Threading.Tasks; namespace Bit.CryptoAgent.Controllers { + [Authorize("Application")] [Route("user-keys")] public class UserKeysController : Controller { private readonly ILogger _logger; private readonly ICryptoService _cryptoService; private readonly IUserKeyRepository _userKeyRepository; + private readonly IdentityOptions _identityOptions; public UserKeysController( + IOptions optionsAccessor, ILogger logger, IUserKeyRepository userKeyRepository, ICryptoService cryptoService) { + _identityOptions = optionsAccessor?.Value ?? new IdentityOptions(); _logger = logger; _cryptoService = cryptoService; _userKeyRepository = userKeyRepository; } - [HttpPost("{userId}/get")] - public async Task Get(Guid userId, [FromBody] UserKeyGetRequestModel model) + [HttpPost("get")] + public async Task Get([FromBody] UserKeyGetRequestModel model) { + var userId = GetProperUserId().Value; var publicKey = Convert.FromBase64String(model.PublicKey); var user = await _userKeyRepository.ReadAsync(userId); if (user == null) @@ -45,9 +54,10 @@ namespace Bit.CryptoAgent.Controllers return new JsonResult(response); } - [HttpPost("{userId}")] - public async Task Post(Guid userId, [FromBody] UserKeyRequestModel model) + [HttpPost] + public async Task Post([FromBody] UserKeyRequestModel model) { + var userId = GetProperUserId().Value; var user = await _userKeyRepository.ReadAsync(userId); if (user != null) { @@ -63,9 +73,10 @@ namespace Bit.CryptoAgent.Controllers return new OkResult(); } - [HttpPut("{userId}")] - public async Task Put(Guid userId, [FromBody] UserKeyRequestModel model) + [HttpPut] + public async Task Put([FromBody] UserKeyRequestModel model) { + var userId = GetProperUserId().Value; var user = await _userKeyRepository.ReadAsync(userId); if (user != null) { @@ -80,5 +91,15 @@ namespace Bit.CryptoAgent.Controllers await _userKeyRepository.UpdateAsync(user); return new OkResult(); } + + public Guid? GetProperUserId() + { + var userId = User.FindFirstValue(_identityOptions.ClaimsIdentity.UserIdClaimType); + if (!Guid.TryParse(userId, out var userIdGuid)) + { + return null; + } + return userIdGuid; + } } } diff --git a/src/CryptoAgent/CryptoAgent.csproj b/src/CryptoAgent/CryptoAgent.csproj index 8df0120..43cedae 100644 --- a/src/CryptoAgent/CryptoAgent.csproj +++ b/src/CryptoAgent/CryptoAgent.csproj @@ -14,6 +14,7 @@ + diff --git a/src/CryptoAgent/CryptoAgentSettings.cs b/src/CryptoAgent/CryptoAgentSettings.cs index 823dfd5..9dd16d3 100644 --- a/src/CryptoAgent/CryptoAgentSettings.cs +++ b/src/CryptoAgent/CryptoAgentSettings.cs @@ -2,6 +2,8 @@ { public class CryptoAgentSettings { + public string IdentityServerUri { get; set; } + public DatabaseSettings Database { get; set; } public CertificateSettings Certificate { get; set; } public RsaKeySettings RsaKey { get; set; } diff --git a/src/CryptoAgent/Startup.cs b/src/CryptoAgent/Startup.cs index c25e56f..cd47f46 100644 --- a/src/CryptoAgent/Startup.cs +++ b/src/CryptoAgent/Startup.cs @@ -1,13 +1,17 @@ using Bit.CryptoAgent.Repositories; using Bit.CryptoAgent.Services; +using IdentityModel; +using IdentityServer4.AccessTokenValidation; using JsonFlatFileDataStore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System; using System.Globalization; +using System.Security.Claims; namespace Bit.CryptoAgent { @@ -29,6 +33,51 @@ namespace Bit.CryptoAgent ConfigurationBinder.Bind(Configuration.GetSection("CryptoAgentSettings"), settings); services.AddSingleton(s => settings); + AddAuthentication(services, settings); + AddRsaKeyProvider(services, settings); + + services.AddSingleton(); + services.AddSingleton(); + + AddDatabase(services, settings); + + services.AddControllers(); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseAuthentication(); + app.UseAuthorization(); + + app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute()); + } + + private void AddDatabase(IServiceCollection services, CryptoAgentSettings settings) + { + var databaseProvider = settings.Database.Provider?.ToLowerInvariant(); + if (databaseProvider == "json") + { + // Assign foobar to keyProperty in order to not use incrementing Id functionality + services.AddSingleton( + new DataStore(settings.Database.JsonFilePath, keyProperty: "--foobar--")); + services.AddSingleton(); + services.AddSingleton(); + } + else + { + throw new Exception("No database configured."); + } + } + + private void AddRsaKeyProvider(IServiceCollection services, CryptoAgentSettings settings) + { var rsaKeyProvider = settings.RsaKey.Provider?.ToLowerInvariant(); if (rsaKeyProvider == "certificate") { @@ -76,36 +125,44 @@ namespace Bit.CryptoAgent { throw new Exception("Unknown rsa key provider configured."); } + } - services.AddSingleton(); - services.AddSingleton(); - - var databaseProvider = settings.Database.Provider?.ToLowerInvariant(); - if (databaseProvider == "json") - { - // Assign foobar to keyProperty in order to not use incrementing Id functionality - services.AddSingleton( - new DataStore(settings.Database.JsonFilePath, keyProperty: "--foobar--")); - services.AddSingleton(); - services.AddSingleton(); - } - else + private void AddAuthentication(IServiceCollection services, CryptoAgentSettings settings) + { + services.Configure(options => { - throw new Exception("No database configured."); - } + options.ClaimsIdentity = new ClaimsIdentityOptions + { + UserNameClaimType = JwtClaimTypes.Email, + UserIdClaimType = JwtClaimTypes.Subject + }; + }); + services + .AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme) + .AddIdentityServerAuthentication(options => + { + options.Authority = settings.IdentityServerUri; + options.RequireHttpsMetadata = !Environment.IsDevelopment() && + settings.IdentityServerUri.StartsWith("https"); + options.NameClaimType = ClaimTypes.Email; + options.SupportedTokens = SupportedTokens.Jwt; + }); - services.AddControllers(); - } + services + .AddAuthorization(config => + { + config.AddPolicy("Application", policy => + { + policy.RequireAuthenticatedUser(); + policy.RequireClaim(JwtClaimTypes.AuthenticationMethod, "Application", "external"); + policy.RequireClaim(JwtClaimTypes.Scope, "api"); + }); + }); - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) + if (Environment.IsDevelopment()) { - app.UseDeveloperExceptionPage(); + Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true; } - - app.UseRouting(); - app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute()); } } } diff --git a/src/CryptoAgent/appsettings.Development.json b/src/CryptoAgent/appsettings.Development.json index 8983e0f..5410f8b 100644 --- a/src/CryptoAgent/appsettings.Development.json +++ b/src/CryptoAgent/appsettings.Development.json @@ -5,5 +5,8 @@ "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } + }, + "cryptoAgentSettings": { + "identityServerUri": "http://localhost:33656/" } } diff --git a/src/CryptoAgent/appsettings.json b/src/CryptoAgent/appsettings.json index 8c3a64a..6f7cc7d 100644 --- a/src/CryptoAgent/appsettings.json +++ b/src/CryptoAgent/appsettings.json @@ -8,6 +8,7 @@ }, "AllowedHosts": "*", "cryptoAgentSettings": { + "identityServerUri": "https://identity.bitwarden.com/", "database": { "provider": "json", "jsonFilePath": "/etc/bitwarden/data.json"