mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 23:52:50 -05:00
[SM-220] Move identity specific files to identity (#2279)
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
using Bit.Admin.Models;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Admin.IdentityServer;
|
||||
using Bit.Admin.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
|
@ -1,11 +1,9 @@
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
namespace Bit.Admin.IdentityServer;
|
||||
|
||||
public class PasswordlessSignInManager<TUser> : SignInManager<TUser> where TUser : class
|
||||
{
|
@ -1,8 +1,7 @@
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
namespace Bit.Admin.IdentityServer;
|
||||
|
||||
public class ReadOnlyEnvIdentityUserStore : ReadOnlyIdentityUserStore
|
||||
{
|
||||
@ -14,7 +13,7 @@ public class ReadOnlyEnvIdentityUserStore : ReadOnlyIdentityUserStore
|
||||
}
|
||||
|
||||
public override Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var usersCsv = _configuration["adminSettings:admins"];
|
||||
if (!CoreHelpers.SettingHasValue(usersCsv))
|
||||
@ -59,7 +58,7 @@ public class ReadOnlyEnvIdentityUserStore : ReadOnlyIdentityUserStore
|
||||
}
|
||||
|
||||
public override Task<IdentityUser> FindByIdAsync(string userId,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return FindByEmailAsync(userId, cancellationToken);
|
||||
}
|
@ -1,108 +1,107 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
namespace Bit.Admin.IdentityServer;
|
||||
|
||||
public abstract class ReadOnlyIdentityUserStore :
|
||||
IUserStore<IdentityUser>,
|
||||
IUserEmailStore<IdentityUser>,
|
||||
IUserSecurityStampStore<IdentityUser>
|
||||
{
|
||||
public void Dispose() { }
|
||||
|
||||
public Task<IdentityResult> CreateAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IdentityResult> DeleteAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public abstract Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
||||
CancellationToken cancellationToken = default(CancellationToken));
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
public abstract Task<IdentityUser> FindByIdAsync(string userId,
|
||||
CancellationToken cancellationToken = default(CancellationToken));
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
public async Task<IdentityUser> FindByNameAsync(string normalizedUserName,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await FindByEmailAsync(normalizedUserName, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<string> GetEmailAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
|
||||
public Task<bool> GetEmailConfirmedAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(user.EmailConfirmed);
|
||||
}
|
||||
|
||||
public Task<string> GetNormalizedEmailAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
|
||||
public Task<string> GetNormalizedUserNameAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
|
||||
public Task<string> GetUserIdAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(user.Id);
|
||||
}
|
||||
|
||||
public Task<string> GetUserNameAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
|
||||
public Task SetEmailAsync(IdentityUser user, string email,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SetEmailConfirmedAsync(IdentityUser user, bool confirmed,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SetNormalizedEmailAsync(IdentityUser user, string normalizedEmail,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
user.NormalizedEmail = normalizedEmail;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task SetNormalizedUserNameAsync(IdentityUser user, string normalizedName,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
user.NormalizedUserName = normalizedName;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task SetUserNameAsync(IdentityUser user, string userName,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IdentityResult> UpdateAsync(IdentityUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
44
src/Admin/IdentityServer/ServiceCollectionExtensions.cs
Normal file
44
src/Admin/IdentityServer/ServiceCollectionExtensions.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace Bit.Admin.IdentityServer;
|
||||
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
public static Tuple<IdentityBuilder, IdentityBuilder> AddPasswordlessIdentityServices<TUserStore>(
|
||||
this IServiceCollection services, GlobalSettings globalSettings) where TUserStore : class
|
||||
{
|
||||
services.TryAddTransient<ILookupNormalizer, LowerInvariantLookupNormalizer>();
|
||||
services.Configure<DataProtectionTokenProviderOptions>(options =>
|
||||
{
|
||||
options.TokenLifespan = TimeSpan.FromMinutes(15);
|
||||
});
|
||||
|
||||
var passwordlessIdentityBuilder = services.AddIdentity<IdentityUser, Role>()
|
||||
.AddUserStore<TUserStore>()
|
||||
.AddRoleStore<RoleStore>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
var regularIdentityBuilder = services.AddIdentityCore<User>()
|
||||
.AddUserStore<UserStore>();
|
||||
|
||||
services.TryAddScoped<PasswordlessSignInManager<IdentityUser>, PasswordlessSignInManager<IdentityUser>>();
|
||||
|
||||
services.ConfigureApplicationCookie(options =>
|
||||
{
|
||||
options.LoginPath = "/login";
|
||||
options.LogoutPath = "/";
|
||||
options.AccessDeniedPath = "/login?accessDenied=true";
|
||||
options.Cookie.Name = $"Bitwarden_{globalSettings.ProjectName}";
|
||||
options.Cookie.HttpOnly = true;
|
||||
options.ExpireTimeSpan = TimeSpan.FromDays(2);
|
||||
options.ReturnUrlParameter = "returnUrl";
|
||||
options.SlidingExpiration = true;
|
||||
});
|
||||
|
||||
return new Tuple<IdentityBuilder, IdentityBuilder>(passwordlessIdentityBuilder, regularIdentityBuilder);
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
using System.Globalization;
|
||||
using Bit.Admin.IdentityServer;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.SharedWeb.Utilities;
|
||||
|
@ -1,38 +0,0 @@
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Bit.Core.Identity;
|
||||
|
||||
public class ReadOnlyDatabaseIdentityUserStore : ReadOnlyIdentityUserStore
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
public ReadOnlyDatabaseIdentityUserStore(
|
||||
IUserService userService,
|
||||
IUserRepository userRepository)
|
||||
{
|
||||
_userService = userService;
|
||||
_userRepository = userRepository;
|
||||
}
|
||||
|
||||
public override async Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var user = await _userRepository.GetByEmailAsync(normalizedEmail);
|
||||
return user?.ToIdentityUser(await _userService.TwoFactorIsEnabledAsync(user));
|
||||
}
|
||||
|
||||
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(await _userService.TwoFactorIsEnabledAsync(user));
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
using Bit.Core.Settings;
|
||||
using IdentityServer4;
|
||||
using IdentityServer4.Models;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
|
||||
public class OidcIdentityClient : Client
|
||||
{
|
||||
public OidcIdentityClient(GlobalSettings globalSettings)
|
||||
{
|
||||
ClientId = "oidc-identity";
|
||||
RequireClientSecret = true;
|
||||
RequirePkce = true;
|
||||
ClientSecrets = new List<Secret> { new Secret(globalSettings.OidcIdentityClientKey.Sha256()) };
|
||||
AllowedScopes = new string[]
|
||||
{
|
||||
IdentityServerConstants.StandardScopes.OpenId,
|
||||
IdentityServerConstants.StandardScopes.Profile
|
||||
};
|
||||
AllowedGrantTypes = GrantTypes.Code;
|
||||
Enabled = true;
|
||||
RedirectUris = new List<string> { $"{globalSettings.BaseServiceUri.Identity}/signin-oidc" };
|
||||
RequireConsent = false;
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using Bit.Core.Settings;
|
||||
using IdentityServer4.Models;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Identity.IdentityServer;
|
||||
|
||||
public class ApiClient : Client
|
||||
{
|
@ -1,7 +1,7 @@
|
||||
using IdentityModel;
|
||||
using IdentityServer4.Models;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Identity.IdentityServer;
|
||||
|
||||
public class ApiResources
|
||||
{
|
@ -1,6 +1,6 @@
|
||||
using IdentityServer4.Models;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Identity.IdentityServer;
|
||||
|
||||
public class ApiScopes
|
||||
{
|
@ -4,9 +4,8 @@ using IdentityServer4.Models;
|
||||
using IdentityServer4.Services;
|
||||
using IdentityServer4.Stores;
|
||||
using IdentityServer4.Stores.Serialization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Identity.IdentityServer;
|
||||
|
||||
// ref: https://raw.githubusercontent.com/IdentityServer/IdentityServer4/3.1.3/src/IdentityServer4/src/Stores/Default/DefaultAuthorizationCodeStore.cs
|
||||
public class AuthorizationCodeStore : DefaultGrantStore<AuthorizationCode>, IAuthorizationCodeStore
|
@ -2,6 +2,7 @@
|
||||
using System.Reflection;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Json;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
@ -15,9 +16,8 @@ using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using IdentityServer4.Validation;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Identity.IdentityServer;
|
||||
|
||||
public abstract class BaseRequestValidator<T> where T : class
|
||||
{
|
@ -10,7 +10,7 @@ using IdentityModel;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Stores;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Identity.IdentityServer;
|
||||
|
||||
public class ClientStore : IClientStore
|
||||
{
|
@ -9,9 +9,8 @@ using IdentityModel;
|
||||
using IdentityServer4.Extensions;
|
||||
using IdentityServer4.Validation;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Identity.IdentityServer;
|
||||
|
||||
public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenRequestValidationContext>,
|
||||
ICustomTokenRequestValidator
|
@ -1,7 +1,7 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Models.Business;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Identity.IdentityServer;
|
||||
|
||||
public class CustomValidatorRequestContext
|
||||
{
|
@ -3,7 +3,7 @@ using IdentityServer4.Models;
|
||||
using IdentityServer4.Stores;
|
||||
using Grant = Bit.Core.Entities.Grant;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Identity.IdentityServer;
|
||||
|
||||
public class PersistedGrantStore : IPersistedGrantStore
|
||||
{
|
@ -6,7 +6,7 @@ using Bit.Core.Utilities;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Services;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Identity.IdentityServer;
|
||||
|
||||
public class ProfileService : IProfileService
|
||||
{
|
@ -9,9 +9,8 @@ using Bit.Core.Utilities;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Validation;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Identity.IdentityServer;
|
||||
|
||||
public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwnerPasswordValidationContext>,
|
||||
IResourceOwnerPasswordValidator
|
@ -2,7 +2,7 @@
|
||||
using Bit.Core.Settings;
|
||||
using IdentityServer4.Models;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Identity.IdentityServer;
|
||||
|
||||
public class StaticClientStore
|
||||
{
|
@ -2,7 +2,7 @@
|
||||
using Bit.Core.Utilities;
|
||||
using IdentityServer4.Services;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Identity.IdentityServer;
|
||||
|
||||
public class CustomCorsPolicyService : ICorsPolicyService
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Identity.IdentityServer;
|
||||
using Bit.SharedWeb.Utilities;
|
||||
using IdentityServer4.ResponseHandling;
|
||||
using IdentityServer4.Services;
|
||||
|
@ -33,7 +33,6 @@ using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Caching.StackExchangeRedis;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
@ -358,39 +357,7 @@ public static class ServiceCollectionExtensions
|
||||
return identityBuilder;
|
||||
}
|
||||
|
||||
public static Tuple<IdentityBuilder, IdentityBuilder> AddPasswordlessIdentityServices<TUserStore>(
|
||||
this IServiceCollection services, GlobalSettings globalSettings) where TUserStore : class
|
||||
{
|
||||
services.TryAddTransient<ILookupNormalizer, LowerInvariantLookupNormalizer>();
|
||||
services.Configure<DataProtectionTokenProviderOptions>(options =>
|
||||
{
|
||||
options.TokenLifespan = TimeSpan.FromMinutes(15);
|
||||
});
|
||||
|
||||
var passwordlessIdentityBuilder = services.AddIdentity<IdentityUser, Role>()
|
||||
.AddUserStore<TUserStore>()
|
||||
.AddRoleStore<RoleStore>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
var regularIdentityBuilder = services.AddIdentityCore<User>()
|
||||
.AddUserStore<UserStore>();
|
||||
|
||||
services.TryAddScoped<PasswordlessSignInManager<IdentityUser>, PasswordlessSignInManager<IdentityUser>>();
|
||||
|
||||
services.ConfigureApplicationCookie(options =>
|
||||
{
|
||||
options.LoginPath = "/login";
|
||||
options.LogoutPath = "/";
|
||||
options.AccessDeniedPath = "/login?accessDenied=true";
|
||||
options.Cookie.Name = $"Bitwarden_{globalSettings.ProjectName}";
|
||||
options.Cookie.HttpOnly = true;
|
||||
options.ExpireTimeSpan = TimeSpan.FromDays(2);
|
||||
options.ReturnUrlParameter = "returnUrl";
|
||||
options.SlidingExpiration = true;
|
||||
});
|
||||
|
||||
return new Tuple<IdentityBuilder, IdentityBuilder>(passwordlessIdentityBuilder, regularIdentityBuilder);
|
||||
}
|
||||
|
||||
public static void AddIdentityAuthenticationServices(
|
||||
this IServiceCollection services, GlobalSettings globalSettings, IWebHostEnvironment environment,
|
||||
|
Reference in New Issue
Block a user