mirror of
https://github.com/bitwarden/server.git
synced 2025-07-02 16:42:50 -05:00
passwordless sign in for admin
This commit is contained in:
37
src/Core/Identity/ReadOnlyDatabaseIdentityUserStore.cs
Normal file
37
src/Core/Identity/ReadOnlyDatabaseIdentityUserStore.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Bit.Core.Repositories;
|
||||
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
public class ReadOnlyDatabaseIdentityUserStore : ReadOnlyIdentityUserStore
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
public ReadOnlyDatabaseIdentityUserStore(IUserRepository userRepository)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
}
|
||||
|
||||
public override async Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var user = await _userRepository.GetByEmailAsync(normalizedEmail);
|
||||
return user?.ToIdentityUser();
|
||||
}
|
||||
|
||||
public override async Task<IdentityUser> FindByIdAsync(string userId,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if(!Guid.TryParse(userId, out var userIdGuid))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var user = await _userRepository.GetByIdAsync(userIdGuid);
|
||||
return user?.ToIdentityUser();
|
||||
}
|
||||
}
|
||||
}
|
53
src/Core/Identity/ReadOnlyEnvIdentityUserStore.cs
Normal file
53
src/Core/Identity/ReadOnlyEnvIdentityUserStore.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
public class ReadOnlyEnvIdentityUserStore : ReadOnlyIdentityUserStore
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public ReadOnlyEnvIdentityUserStore(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public override Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var usersCsv = _configuration["adminSettings:admins"];
|
||||
if(string.IsNullOrWhiteSpace(usersCsv))
|
||||
{
|
||||
return Task.FromResult<IdentityUser>(null);
|
||||
}
|
||||
|
||||
var users = usersCsv.ToLowerInvariant().Split(',');
|
||||
var user = users.Where(a => a.Trim() == normalizedEmail).FirstOrDefault();
|
||||
if(user == null || !user.Contains("@"))
|
||||
{
|
||||
return Task.FromResult<IdentityUser>(null);
|
||||
}
|
||||
|
||||
user = user.Trim();
|
||||
return Task.FromResult(new IdentityUser
|
||||
{
|
||||
Id = user,
|
||||
Email = user,
|
||||
NormalizedEmail = user,
|
||||
EmailConfirmed = true,
|
||||
UserName = user,
|
||||
NormalizedUserName = user,
|
||||
SecurityStamp = user
|
||||
});
|
||||
}
|
||||
|
||||
public override Task<IdentityUser> FindByIdAsync(string userId,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return FindByEmailAsync(userId, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,22 +2,14 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Bit.Core.Repositories;
|
||||
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
public class ReadOnlyIdentityUserStore :
|
||||
public abstract class ReadOnlyIdentityUserStore :
|
||||
IUserStore<IdentityUser>,
|
||||
IUserEmailStore<IdentityUser>,
|
||||
IUserSecurityStampStore<IdentityUser>
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
public ReadOnlyIdentityUserStore(IUserRepository userRepository)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public Task<IdentityResult> CreateAsync(IdentityUser user,
|
||||
@ -32,24 +24,11 @@ namespace Bit.Core.Identity
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var user = await _userRepository.GetByEmailAsync(normalizedEmail);
|
||||
return user?.ToIdentityUser();
|
||||
}
|
||||
public abstract Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
||||
CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
public async Task<IdentityUser> FindByIdAsync(string userId,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if(!Guid.TryParse(userId, out var userIdGuid))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var user = await _userRepository.GetByIdAsync(userIdGuid);
|
||||
return user?.ToIdentityUser();
|
||||
}
|
||||
public abstract Task<IdentityUser> FindByIdAsync(string userId,
|
||||
CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
public async Task<IdentityUser> FindByNameAsync(string normalizedUserName,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
|
23
src/Core/Utilities/LoggingExceptionHandlerFilterAttribute.cs
Normal file
23
src/Core/Utilities/LoggingExceptionHandlerFilterAttribute.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Utilities
|
||||
{
|
||||
public class LoggingExceptionHandlerFilterAttribute : ExceptionFilterAttribute
|
||||
{
|
||||
public override void OnException(ExceptionContext context)
|
||||
{
|
||||
var exception = context.Exception;
|
||||
if(exception == null)
|
||||
{
|
||||
// Should never happen.
|
||||
return;
|
||||
}
|
||||
|
||||
var logger = context.HttpContext.RequestServices
|
||||
.GetRequiredService<ILogger<LoggingExceptionHandlerFilterAttribute>>();
|
||||
logger.LogError(0, exception, exception.Message);
|
||||
}
|
||||
}
|
||||
}
|
@ -11,7 +11,6 @@ using IdentityServer4.Validation;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@ -22,6 +21,7 @@ using System.IO;
|
||||
using SqlServerRepos = Bit.Core.Repositories.SqlServer;
|
||||
using System.Threading.Tasks;
|
||||
using TableStorageRepos = Bit.Core.Repositories.TableStorage;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace Bit.Core.Utilities
|
||||
{
|
||||
@ -199,6 +199,38 @@ namespace Bit.Core.Utilities
|
||||
return identityBuilder;
|
||||
}
|
||||
|
||||
public static IdentityBuilder AddPasswordlessIdentityServices<TUserStore>(
|
||||
this IServiceCollection services, GlobalSettings globalSettings) where TUserStore : class
|
||||
{
|
||||
services.AddTransient<ILookupNormalizer, LowerInvariantLookupNormalizer>();
|
||||
|
||||
services.Configure<DataProtectionTokenProviderOptions>(options =>
|
||||
{
|
||||
options.TokenLifespan = TimeSpan.FromMinutes(15);
|
||||
});
|
||||
|
||||
var identityBuilder = services.AddIdentity<IdentityUser, Role>()
|
||||
.AddUserStore<TUserStore>()
|
||||
.AddRoleStore<RoleStore>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
services.TryAddScoped<PasswordlessSignInManager<IdentityUser>, PasswordlessSignInManager<IdentityUser>>();
|
||||
|
||||
services.ConfigureApplicationCookie(options =>
|
||||
{
|
||||
options.LoginPath = "/login";
|
||||
options.LogoutPath = "/";
|
||||
options.AccessDeniedPath = "/login?accessDenied=1";
|
||||
options.Cookie.Name = $"Bitwarden_{globalSettings.ProjectName}";
|
||||
options.Cookie.HttpOnly = true;
|
||||
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
|
||||
options.ReturnUrlParameter = "returnUrl";
|
||||
options.SlidingExpiration = true;
|
||||
});
|
||||
|
||||
return identityBuilder;
|
||||
}
|
||||
|
||||
public static IIdentityServerBuilder AddCustomIdentityServerServices(
|
||||
this IServiceCollection services, IHostingEnvironment env, GlobalSettings globalSettings)
|
||||
{
|
||||
|
Reference in New Issue
Block a user