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 LicenseDirectory { get; set; }
public virtual string PushRelayBaseUri { get; set; }
public virtual string InternalIdentityKey { get; set; }
public virtual bool DisableUserRegistration { get; set; }
public virtual InstallationSettings Installation { get; set; } = new InstallationSettings();
public virtual BaseServiceUriSettings BaseServiceUri { get; set; } = new BaseServiceUriSettings();

View File

@ -6,6 +6,7 @@ using Bit.Core.Repositories;
using System;
using System.Security.Claims;
using IdentityModel;
using Bit.Core.Utilities;
namespace Bit.Core.IdentityServer
{
@ -14,15 +15,19 @@ namespace Bit.Core.IdentityServer
private static IDictionary<string, Client> _apiClients = StaticClients.GetApiClients();
private readonly IInstallationRepository _installationRepository;
private readonly GlobalSettings _globalSettings;
public ClientStore(
IInstallationRepository installationRepository)
IInstallationRepository installationRepository,
GlobalSettings globalSettings)
{
_installationRepository = installationRepository;
_globalSettings = globalSettings;
}
public async Task<Client> FindClientByIdAsync(string clientId)
{
if(clientId.StartsWith("installation."))
if(!_globalSettings.SelfHosted && clientId.StartsWith("installation."))
{
var idParts = clientId.Split('.');
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;
}

View File

@ -12,35 +12,44 @@ using Microsoft.Extensions.Logging;
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 DateTime? _nextAuthAttempt = null;
private readonly ILogger<BaseRelayPushNotificationService> _logger;
public BaseRelayPushNotificationService(
GlobalSettings globalSettings,
ILogger<BaseRelayPushNotificationService> logger)
public BaseIdentityClientService(
string baseClientServerUri,
string baseIdentityServerUri,
string identityScope,
string identityClientId,
string identityClientSecret,
ILogger<BaseIdentityClientService> logger)
{
_identityScope = identityScope;
_identityClientId = identityClientId;
_identityClientSecret = identityClientSecret;
_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
{
BaseAddress = new Uri(globalSettings.Installation.IdentityUri)
BaseAddress = new Uri(baseIdentityServerUri)
};
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 GlobalSettings GlobalSettings { get; private set; }
protected string AccessToken { get; private set; }
protected async Task<bool> HandleTokenStateAsync()
@ -63,9 +72,9 @@ namespace Bit.Core.Services
Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "client_credentials" },
{ "scope", "api.push" },
{ "client_id", $"installation.{GlobalSettings.Installation.Id}" },
{ "client_secret", $"{GlobalSettings.Installation.Key}" }
{ "scope", _identityScope },
{ "client_id", _identityClientId },
{ "client_secret", _identityClientSecret }
})
};
@ -76,7 +85,7 @@ namespace Bit.Core.Services
}
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)

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
using Bit.Core;
using Bit.Core.Services;
using Bit.Core.Utilities;
using IdentityModel;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
@ -36,7 +37,15 @@ namespace Bit.Events
services.AddScoped<CurrentContext>();
// 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.AddScoped<IEventService, EventService>();

View File

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

View File

@ -1,5 +1,6 @@
using Bit.Core;
using Bit.Core.Utilities;
using IdentityModel;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SignalR;
@ -37,7 +38,20 @@ namespace Bit.Hub
services.AddScoped<CurrentContext>();
// 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
services.AddSignalR();

View File

@ -49,7 +49,8 @@ namespace Bit.Setup
["globalSettings__dataProtection__directory"] = $"{OutputDirectory}/core/aspnet-dataprotection",
["globalSettings__logDirectory"] = $"{OutputDirectory}/logs",
["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__key"] = InstallationKey,
["globalSettings__yubico__clientId"] = "REPLACE",