1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-22 12:15:36 +01:00

internal identity authorization

This commit is contained in:
Kyle Spearrin 2018-08-15 18:43:26 -04:00
parent 25899fd326
commit ff01ce5ca7
11 changed files with 119 additions and 56 deletions

View File

@ -11,6 +11,7 @@ namespace Bit.Core
public virtual string LogDirectory { get; set; } public virtual string LogDirectory { get; set; }
public virtual string LicenseDirectory { get; set; } public virtual string LicenseDirectory { get; set; }
public virtual string PushRelayBaseUri { get; set; } public virtual string PushRelayBaseUri { get; set; }
public virtual string InternalIdentityKey { get; set; }
public virtual bool DisableUserRegistration { get; set; } public virtual bool DisableUserRegistration { get; set; }
public virtual InstallationSettings Installation { get; set; } = new InstallationSettings(); public virtual InstallationSettings Installation { get; set; } = new InstallationSettings();
public virtual BaseServiceUriSettings BaseServiceUri { get; set; } = new BaseServiceUriSettings(); public virtual BaseServiceUriSettings BaseServiceUri { get; set; } = new BaseServiceUriSettings();

View File

@ -6,6 +6,7 @@ using Bit.Core.Repositories;
using System; using System;
using System.Security.Claims; using System.Security.Claims;
using IdentityModel; using IdentityModel;
using Bit.Core.Utilities;
namespace Bit.Core.IdentityServer namespace Bit.Core.IdentityServer
{ {
@ -14,15 +15,19 @@ namespace Bit.Core.IdentityServer
private static IDictionary<string, Client> _apiClients = StaticClients.GetApiClients(); private static IDictionary<string, Client> _apiClients = StaticClients.GetApiClients();
private readonly IInstallationRepository _installationRepository; private readonly IInstallationRepository _installationRepository;
private readonly GlobalSettings _globalSettings;
public ClientStore( public ClientStore(
IInstallationRepository installationRepository) IInstallationRepository installationRepository,
GlobalSettings globalSettings)
{ {
_installationRepository = installationRepository; _installationRepository = installationRepository;
_globalSettings = globalSettings;
} }
public async Task<Client> FindClientByIdAsync(string clientId) public async Task<Client> FindClientByIdAsync(string clientId)
{ {
if(clientId.StartsWith("installation.")) if(!_globalSettings.SelfHosted && clientId.StartsWith("installation."))
{ {
var idParts = clientId.Split('.'); var idParts = clientId.Split('.');
if(idParts.Length > 1 && Guid.TryParse(idParts[1], out Guid id)) if(idParts.Length > 1 && Guid.TryParse(idParts[1], out Guid id))
@ -44,6 +49,29 @@ namespace Bit.Core.IdentityServer
} }
} }
} }
else if(_globalSettings.SelfHosted && clientId.StartsWith("internal.") &&
CoreHelpers.SettingHasValue(_globalSettings.InternalIdentityKey))
{
var idParts = clientId.Split('.');
if(idParts.Length > 1)
{
var id = idParts[1];
if(!string.IsNullOrWhiteSpace(id))
{
return new Client
{
ClientId = $"internal.{id}",
RequireClientSecret = true,
ClientSecrets = { new Secret(_globalSettings.InternalIdentityKey.Sha256()) },
AllowedScopes = new string[] { "internal" },
AllowedGrantTypes = GrantTypes.ClientCredentials,
AccessTokenLifetime = 3600 * 24,
Enabled = true,
Claims = new List<Claim> { new Claim(JwtClaimTypes.Subject, id) }
};
}
}
}
return _apiClients.ContainsKey(clientId) ? _apiClients[clientId] : null; return _apiClients.ContainsKey(clientId) ? _apiClients[clientId] : null;
} }

View File

@ -12,35 +12,44 @@ using Microsoft.Extensions.Logging;
namespace Bit.Core.Services namespace Bit.Core.Services
{ {
public abstract class BaseRelayPushNotificationService public abstract class BaseIdentityClientService
{ {
private readonly string _identityScope;
private readonly string _identityClientId;
private readonly string _identityClientSecret;
private readonly ILogger<BaseIdentityClientService> _logger;
private dynamic _decodedToken; private dynamic _decodedToken;
private DateTime? _nextAuthAttempt = null; private DateTime? _nextAuthAttempt = null;
private readonly ILogger<BaseRelayPushNotificationService> _logger;
public BaseRelayPushNotificationService( public BaseIdentityClientService(
GlobalSettings globalSettings, string baseClientServerUri,
ILogger<BaseRelayPushNotificationService> logger) string baseIdentityServerUri,
string identityScope,
string identityClientId,
string identityClientSecret,
ILogger<BaseIdentityClientService> logger)
{ {
_identityScope = identityScope;
_identityClientId = identityClientId;
_identityClientSecret = identityClientSecret;
_logger = logger; _logger = logger;
GlobalSettings = globalSettings;
PushClient = new HttpClient Client = new HttpClient
{ {
BaseAddress = new Uri(globalSettings.PushRelayBaseUri) BaseAddress = new Uri(baseClientServerUri)
}; };
PushClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
IdentityClient = new HttpClient IdentityClient = new HttpClient
{ {
BaseAddress = new Uri(globalSettings.Installation.IdentityUri) BaseAddress = new Uri(baseIdentityServerUri)
}; };
IdentityClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); IdentityClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
} }
protected HttpClient PushClient { get; private set; } protected HttpClient Client { get; private set; }
protected HttpClient IdentityClient { get; private set; } protected HttpClient IdentityClient { get; private set; }
protected GlobalSettings GlobalSettings { get; private set; }
protected string AccessToken { get; private set; } protected string AccessToken { get; private set; }
protected async Task<bool> HandleTokenStateAsync() protected async Task<bool> HandleTokenStateAsync()
@ -63,9 +72,9 @@ namespace Bit.Core.Services
Content = new FormUrlEncodedContent(new Dictionary<string, string> Content = new FormUrlEncodedContent(new Dictionary<string, string>
{ {
{ "grant_type", "client_credentials" }, { "grant_type", "client_credentials" },
{ "scope", "api.push" }, { "scope", _identityScope },
{ "client_id", $"installation.{GlobalSettings.Installation.Id}" }, { "client_id", _identityClientId },
{ "client_secret", $"{GlobalSettings.Installation.Key}" } { "client_secret", _identityClientSecret }
}) })
}; };
@ -76,7 +85,7 @@ namespace Bit.Core.Services
} }
catch(Exception e) catch(Exception e)
{ {
_logger.LogError(12339, e, "Unable to auth for push."); _logger.LogError(12339, e, "Unable to authenticate with identity server.");
} }
if(response == null) if(response == null)

View File

@ -10,7 +10,7 @@ using Microsoft.Extensions.Logging;
namespace Bit.Core.Services namespace Bit.Core.Services
{ {
public class RelayPushNotificationService : BaseRelayPushNotificationService, IPushNotificationService public class RelayPushNotificationService : BaseIdentityClientService, IPushNotificationService
{ {
private readonly IHttpContextAccessor _httpContextAccessor; private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<RelayPushNotificationService> _logger; private readonly ILogger<RelayPushNotificationService> _logger;
@ -19,7 +19,13 @@ namespace Bit.Core.Services
GlobalSettings globalSettings, GlobalSettings globalSettings,
IHttpContextAccessor httpContextAccessor, IHttpContextAccessor httpContextAccessor,
ILogger<RelayPushNotificationService> logger) ILogger<RelayPushNotificationService> logger)
: base(globalSettings, logger) : base(
globalSettings.PushRelayBaseUri,
globalSettings.Installation.IdentityUri,
"api.push",
$"installation.{globalSettings.Installation.Id}",
globalSettings.Installation.Key,
logger)
{ {
_httpContextAccessor = httpContextAccessor; _httpContextAccessor = httpContextAccessor;
_logger = logger; _logger = logger;
@ -168,12 +174,12 @@ namespace Bit.Core.Services
var message = new TokenHttpRequestMessage(requestModel, AccessToken) var message = new TokenHttpRequestMessage(requestModel, AccessToken)
{ {
Method = HttpMethod.Post, Method = HttpMethod.Post,
RequestUri = new Uri(string.Concat(PushClient.BaseAddress, "/push/send")) RequestUri = new Uri(string.Concat(Client.BaseAddress, "/push/send"))
}; };
try try
{ {
await PushClient.SendAsync(message); await Client.SendAsync(message);
} }
catch(Exception e) catch(Exception e)
{ {

View File

@ -9,14 +9,20 @@ using Microsoft.Extensions.Logging;
namespace Bit.Core.Services namespace Bit.Core.Services
{ {
public class RelayPushRegistrationService : BaseRelayPushNotificationService, IPushRegistrationService public class RelayPushRegistrationService : BaseIdentityClientService, IPushRegistrationService
{ {
private readonly ILogger<RelayPushRegistrationService> _logger; private readonly ILogger<RelayPushRegistrationService> _logger;
public RelayPushRegistrationService( public RelayPushRegistrationService(
GlobalSettings globalSettings, GlobalSettings globalSettings,
ILogger<RelayPushRegistrationService> logger) ILogger<RelayPushRegistrationService> logger)
: base(globalSettings, logger) : base(
globalSettings.PushRelayBaseUri,
globalSettings.Installation.IdentityUri,
"api.push",
$"installation.{globalSettings.Installation.Id}",
globalSettings.Installation.Key,
logger)
{ {
_logger = logger; _logger = logger;
} }
@ -42,12 +48,12 @@ namespace Bit.Core.Services
var message = new TokenHttpRequestMessage(requestModel, AccessToken) var message = new TokenHttpRequestMessage(requestModel, AccessToken)
{ {
Method = HttpMethod.Post, Method = HttpMethod.Post,
RequestUri = new Uri(string.Concat(PushClient.BaseAddress, "/push/register")) RequestUri = new Uri(string.Concat(Client.BaseAddress, "/push/register"))
}; };
try try
{ {
await PushClient.SendAsync(message); await Client.SendAsync(message);
} }
catch(Exception e) catch(Exception e)
{ {
@ -66,12 +72,12 @@ namespace Bit.Core.Services
var message = new TokenHttpRequestMessage(AccessToken) var message = new TokenHttpRequestMessage(AccessToken)
{ {
Method = HttpMethod.Delete, Method = HttpMethod.Delete,
RequestUri = new Uri(string.Concat(PushClient.BaseAddress, "/push/", deviceId)) RequestUri = new Uri(string.Concat(Client.BaseAddress, "/push/", deviceId))
}; };
try try
{ {
await PushClient.SendAsync(message); await Client.SendAsync(message);
} }
catch(Exception e) catch(Exception e)
{ {
@ -96,12 +102,12 @@ namespace Bit.Core.Services
var message = new TokenHttpRequestMessage(requestModel, AccessToken) var message = new TokenHttpRequestMessage(requestModel, AccessToken)
{ {
Method = HttpMethod.Put, Method = HttpMethod.Put,
RequestUri = new Uri(string.Concat(PushClient.BaseAddress, "/push/add-organization")) RequestUri = new Uri(string.Concat(Client.BaseAddress, "/push/add-organization"))
}; };
try try
{ {
await PushClient.SendAsync(message); await Client.SendAsync(message);
} }
catch(Exception e) catch(Exception e)
{ {
@ -126,12 +132,12 @@ namespace Bit.Core.Services
var message = new TokenHttpRequestMessage(requestModel, AccessToken) var message = new TokenHttpRequestMessage(requestModel, AccessToken)
{ {
Method = HttpMethod.Put, Method = HttpMethod.Put,
RequestUri = new Uri(string.Concat(PushClient.BaseAddress, "/push/delete-organization")) RequestUri = new Uri(string.Concat(Client.BaseAddress, "/push/delete-organization"))
}; };
try try
{ {
await PushClient.SendAsync(message); await Client.SendAsync(message);
} }
catch(Exception e) catch(Exception e)
{ {

View File

@ -311,12 +311,9 @@ namespace Bit.Core.Utilities
public static bool SettingHasValue(string setting) public static bool SettingHasValue(string setting)
{ {
if(string.IsNullOrWhiteSpace(setting) || setting.Equals("SECRET") || setting.Equals("REPLACE")) var normalizedSetting = setting?.ToLowerInvariant();
{ return !string.IsNullOrWhiteSpace(normalizedSetting) && !normalizedSetting.Equals("secret") &&
return false; !normalizedSetting.Equals("replace");
}
return true;
} }
public static string Base64UrlEncode(byte[] input) public static string Base64UrlEncode(byte[] input)

View File

@ -234,7 +234,7 @@ namespace Bit.Core.Utilities
public static void AddIdentityAuthenticationServices( public static void AddIdentityAuthenticationServices(
this IServiceCollection services, GlobalSettings globalSettings, IHostingEnvironment environment, this IServiceCollection services, GlobalSettings globalSettings, IHostingEnvironment environment,
Action<AuthorizationOptions> addAuthorization = null) Action<AuthorizationOptions> addAuthorization)
{ {
services services
.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme) .AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
@ -248,21 +248,13 @@ namespace Bit.Core.Utilities
options.SupportedTokens = SupportedTokens.Jwt; options.SupportedTokens = SupportedTokens.Jwt;
}); });
services.AddAuthorization(config => if(addAuthorization != null)
{ {
if(addAuthorization != null) services.AddAuthorization(config =>
{ {
addAuthorization?.Invoke(config); addAuthorization.Invoke(config);
} });
else }
{
config.AddPolicy("Application", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim(JwtClaimTypes.AuthenticationMethod, "Application");
});
}
});
} }
public static IIdentityServerBuilder AddCustomIdentityServerServices( public static IIdentityServerBuilder AddCustomIdentityServerServices(

View File

@ -1,6 +1,7 @@
using Bit.Core; using Bit.Core;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using IdentityModel;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@ -36,7 +37,15 @@ namespace Bit.Events
services.AddScoped<CurrentContext>(); services.AddScoped<CurrentContext>();
// Identity // Identity
services.AddIdentityAuthenticationServices(globalSettings, Environment); services.AddIdentityAuthenticationServices(globalSettings, Environment, config =>
{
config.AddPolicy("Application", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim(JwtClaimTypes.AuthenticationMethod, "Application");
policy.RequireClaim(JwtClaimTypes.Scope, "api");
});
});
// Services // Services
services.AddScoped<IEventService, EventService>(); services.AddScoped<IEventService, EventService>();

View File

@ -6,7 +6,7 @@ using Microsoft.AspNetCore.SignalR;
namespace Bit.Hub namespace Bit.Hub
{ {
[Authorize("Application")] [Authorize("Internal")]
public class EventsController : Controller public class EventsController : Controller
{ {
private readonly IHubContext<SyncHub> _syncHubContext; private readonly IHubContext<SyncHub> _syncHubContext;

View File

@ -1,5 +1,6 @@
using Bit.Core; using Bit.Core;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using IdentityModel;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
@ -37,7 +38,20 @@ namespace Bit.Hub
services.AddScoped<CurrentContext>(); services.AddScoped<CurrentContext>();
// Identity // Identity
services.AddIdentityAuthenticationServices(globalSettings, Environment); services.AddIdentityAuthenticationServices(globalSettings, Environment, config =>
{
config.AddPolicy("Application", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim(JwtClaimTypes.AuthenticationMethod, "Application");
policy.RequireClaim(JwtClaimTypes.Scope, "api");
});
config.AddPolicy("Internal", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim(JwtClaimTypes.Scope, "internal");
});
});
// SignalR // SignalR
services.AddSignalR(); services.AddSignalR();

View File

@ -49,7 +49,8 @@ namespace Bit.Setup
["globalSettings__dataProtection__directory"] = $"{OutputDirectory}/core/aspnet-dataprotection", ["globalSettings__dataProtection__directory"] = $"{OutputDirectory}/core/aspnet-dataprotection",
["globalSettings__logDirectory"] = $"{OutputDirectory}/logs", ["globalSettings__logDirectory"] = $"{OutputDirectory}/logs",
["globalSettings__licenseDirectory"] = $"{OutputDirectory}/core/licenses", ["globalSettings__licenseDirectory"] = $"{OutputDirectory}/core/licenses",
["globalSettings__duo__aKey"] = $"{Helpers.SecureRandomString(64, alpha: true, numeric: true)}", ["globalSettings__internalIdentityKey"] = Helpers.SecureRandomString(64, alpha: true, numeric: true),
["globalSettings__duo__aKey"] = Helpers.SecureRandomString(64, alpha: true, numeric: true),
["globalSettings__installation__id"] = InstallationId?.ToString(), ["globalSettings__installation__id"] = InstallationId?.ToString(),
["globalSettings__installation__key"] = InstallationKey, ["globalSettings__installation__key"] = InstallationKey,
["globalSettings__yubico__clientId"] = "REPLACE", ["globalSettings__yubico__clientId"] = "REPLACE",