Browse Source

More account controller features.

pull/65/head
Sebastian Stehle 9 years ago
parent
commit
86b26fdd12
  1. 8
      src/Squidex.Core/Squidex.Core.csproj
  2. 2
      src/Squidex.Events/Squidex.Events.csproj
  3. 2
      src/Squidex.Infrastructure.Redis/Squidex.Infrastructure.Redis.csproj
  4. 27
      src/Squidex.Infrastructure/GravatarHelper.cs
  5. 6
      src/Squidex.Infrastructure/Squidex.Infrastructure.csproj
  6. 4
      src/Squidex.Read.MongoDb/Squidex.Read.MongoDb.csproj
  7. 74
      src/Squidex.Read.MongoDb/Users/MongoUserRepository.cs
  8. 4
      src/Squidex.Read/Squidex.Read.csproj
  9. 4
      src/Squidex.Read/Users/Repositories/IUserRepository.cs
  10. 2
      src/Squidex.Write/Squidex.Write.csproj
  11. 41
      src/Squidex/Config/Identity/MicrosoftHandler.cs
  12. 39
      src/Squidex/Config/Identity/MicrosoftIdentityUsage.cs
  13. 11
      src/Squidex/Config/Identity/MyIdentityOptions.cs
  14. 2
      src/Squidex/Config/Swagger/SwaggerServices.cs
  15. 15
      src/Squidex/Config/Swagger/XmlResponseTypesProcessor.cs
  16. 25
      src/Squidex/Controllers/Api/Users/Models/CreateUserDto.cs
  17. 24
      src/Squidex/Controllers/Api/Users/Models/UpdateUserDto.cs
  18. 22
      src/Squidex/Controllers/Api/Users/UserManagementController.cs
  19. 6
      src/Squidex/Controllers/ContentApi/Generator/SchemasSwaggerGenerator.cs
  20. 66
      src/Squidex/Controllers/UI/Account/AccountController.cs
  21. 21
      src/Squidex/Controllers/UI/Account/LoginModel.cs
  22. 8
      src/Squidex/Controllers/UI/Account/LoginVM.cs
  23. 47
      src/Squidex/Squidex.csproj
  24. 1
      src/Squidex/Startup.cs
  25. 109
      src/Squidex/Views/Account/Login.cshtml
  26. 14
      src/Squidex/app/theme/_bootstrap.scss
  27. 89
      src/Squidex/app/theme/_static.scss
  28. 4
      src/Squidex/app/theme/_vars.scss
  29. 10
      src/Squidex/app/theme/icomoon/demo-files/demo.css
  30. 1018
      src/Squidex/app/theme/icomoon/demo.html
  31. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.eot
  32. 3
      src/Squidex/app/theme/icomoon/fonts/icomoon.svg
  33. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.ttf
  34. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.woff
  35. 1451
      src/Squidex/app/theme/icomoon/selection.json
  36. 215
      src/Squidex/app/theme/icomoon/style.css
  37. 3
      src/Squidex/appsettings.json
  38. 2
      tests/RunCoverage.ps1
  39. 2
      tests/Squidex.Core.Tests/Squidex.Core.Tests.csproj
  40. 6
      tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj
  41. 2
      tests/Squidex.Read.Tests/Squidex.Read.Tests.csproj
  42. 2
      tests/Squidex.Write.Tests/Squidex.Write.Tests.csproj

8
src/Squidex.Core/Squidex.Core.csproj

@ -11,11 +11,11 @@
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="protobuf-net" Version="2.1.0" />
<PackageReference Include="protobuf-net" Version="2.2.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.3.1" />
<PackageReference Include="NodaTime" Version="2.0.0" />
<PackageReference Include="NJsonSchema" Version="8.34.6331.29178" />
<PackageReference Include="System.ValueTuple" Version="4.3.0" />
<PackageReference Include="NodaTime" Version="2.0.2" />
<PackageReference Include="NJsonSchema" Version="9.1.2" />
<PackageReference Include="System.ValueTuple" Version="4.3.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.6' ">
<PackageReference Include="Microsoft.OData.Core" Version="6.15.0" />

2
src/Squidex.Events/Squidex.Events.csproj

@ -12,6 +12,6 @@
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NodaTime" Version="2.0.0" />
<PackageReference Include="NodaTime" Version="2.0.2" />
</ItemGroup>
</Project>

2
src/Squidex.Infrastructure.Redis/Squidex.Infrastructure.Redis.csproj

@ -10,6 +10,6 @@
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StackExchange.Redis.StrongName" Version="1.2.1" />
<PackageReference Include="StackExchange.Redis.StrongName" Version="1.2.3" />
</ItemGroup>
</Project>

27
src/Squidex.Infrastructure/GravatarHelper.cs

@ -0,0 +1,27 @@
// ==========================================================================
// GravatarHelper.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Security.Cryptography;
using System.Text;
namespace Squidex.Infrastructure
{
public static class GravatarHelper
{
public static string CreatePictureUrl(string email)
{
using (var md5 = MD5.Create())
{
var gravatarHash = md5.ComputeHash(Encoding.UTF8.GetBytes(email.ToLowerInvariant().Trim()));
var gravatarUrl = $"https://www.gravatar.com/avatar/{gravatarHash}";
return gravatarUrl;
}
}
}
}

6
src/Squidex.Infrastructure/Squidex.Infrastructure.csproj

@ -9,10 +9,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ImageSharp" Version="1.0.0-alpha8-00049" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.2" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
<PackageReference Include="NodaTime" Version="2.0.0" />
<PackageReference Include="NodaTime" Version="2.0.2" />
<PackageReference Include="System.Linq" Version="4.3.0" />
<PackageReference Include="System.Reactive" Version="3.1.1" />
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.3.0" />

4
src/Squidex.Read.MongoDb/Squidex.Read.MongoDb.csproj

@ -15,8 +15,8 @@
<ProjectReference Include="..\Squidex.Read\Squidex.Read.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="IdentityServer4" Version="1.5.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="1.1.1" />
<PackageReference Include="IdentityServer4" Version="1.5.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Identity.MongoDB" Version="1.0.2" />
<PackageReference Include="MongoDB.Driver" Version="2.4.3" />
</ItemGroup>

74
src/Squidex.Read.MongoDb/Users/MongoUserRepository.cs

@ -9,13 +9,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.MongoDB;
using Squidex.Core.Identity;
using Squidex.Infrastructure;
using Squidex.Read.Users;
using Squidex.Read.Users.Repositories;
// ReSharper disable ImplicitlyCapturedClosure
// ReSharper disable InvertIf
namespace Squidex.Read.MongoDb.Users
@ -52,6 +55,63 @@ namespace Squidex.Read.MongoDb.Users
return user != null ? new MongoUserEntity(user) : null;
}
public async Task<string> CreateAsync(string email, string displayName, string password)
{
var pictureUrl = GravatarHelper.CreatePictureUrl(email);
var user = new IdentityUser
{
Email = email,
Claims = new List<IdentityUserClaim>
{
new IdentityUserClaim(new Claim(SquidexClaimTypes.SquidexPictureUrl, pictureUrl)),
new IdentityUserClaim(new Claim(SquidexClaimTypes.SquidexDisplayName, displayName))
},
UserName = email
};
await DoChecked(() => userManager.CreateAsync(user), "Cannot create user.");
if (!string.IsNullOrWhiteSpace(password))
{
await DoChecked(() => userManager.AddPasswordAsync(user, password), "Cannot create user.");
}
return user.Id;
}
public async Task UpdateAsync(string id, string email, string displayName, string password)
{
var user = await userManager.FindByIdAsync(id);
if (user == null)
{
throw new DomainObjectNotFoundException(id, typeof(IdentityUser));
}
if (!string.IsNullOrWhiteSpace(email))
{
user.Email = user.UserName = email;
}
if (!string.IsNullOrWhiteSpace(displayName))
{
user.Claims.Find(x => x.Type == SquidexClaimTypes.SquidexDisplayName).Value = displayName;
}
if (!string.IsNullOrWhiteSpace(password))
{
user.PasswordHash = null;
}
await DoChecked(() => userManager.UpdateAsync(user), "Cannot update user.");
if (!string.IsNullOrWhiteSpace(password))
{
await DoChecked(() => userManager.AddPasswordAsync(user, password), "Cannot update user.");
}
}
public async Task LockAsync(string id)
{
var user = await userManager.FindByIdAsync(id);
@ -61,7 +121,7 @@ namespace Squidex.Read.MongoDb.Users
throw new DomainObjectNotFoundException(id, typeof(IdentityUser));
}
await userManager.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.AddYears(100));
await DoChecked(() => userManager.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.AddYears(100)), "Cannot lock user.");
}
public async Task UnlockAsync(string id)
@ -73,7 +133,17 @@ namespace Squidex.Read.MongoDb.Users
throw new DomainObjectNotFoundException(id, typeof(IdentityUser));
}
await userManager.SetLockoutEndDateAsync(user, null);
await DoChecked(() => userManager.SetLockoutEndDateAsync(user, null), "Cannot unlock user.");
}
private static async Task DoChecked(Func<Task<IdentityResult>> action, string message)
{
var result = await action();
if (!result.Succeeded)
{
throw new ValidationException(message, result.Errors.Select(x => new ValidationError(x.Description)).ToArray());
}
}
private IQueryable<IdentityUser> QueryUsers(string email = null)

4
src/Squidex.Read/Squidex.Read.csproj

@ -13,7 +13,7 @@
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.1" />
<PackageReference Include="NodaTime" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.2" />
<PackageReference Include="NodaTime" Version="2.0.2" />
</ItemGroup>
</Project>

4
src/Squidex.Read/Users/Repositories/IUserRepository.cs

@ -17,6 +17,10 @@ namespace Squidex.Read.Users.Repositories
Task<IUserEntity> FindUserByIdAsync(string id);
Task<string> CreateAsync(string email, string displayName, string password);
Task UpdateAsync(string id, string email, string displayName, string password);
Task LockAsync(string id);
Task UnlockAsync(string id);

2
src/Squidex.Write/Squidex.Write.csproj

@ -14,6 +14,6 @@
<ProjectReference Include="..\Squidex.Read\Squidex.Read.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NodaTime" Version="2.0.0" />
<PackageReference Include="NodaTime" Version="2.0.2" />
</ItemGroup>
</Project>

41
src/Squidex/Config/Identity/MicrosoftHandler.cs

@ -0,0 +1,41 @@
// ==========================================================================
// MicrosoftHandler.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.OAuth;
using Squidex.Core.Identity;
// ReSharper disable InvertIf
namespace Squidex.Config.Identity
{
public sealed class MicrosoftHandler : OAuthEvents
{
public override Task CreatingTicket(OAuthCreatingTicketContext context)
{
var displayName = context.User.Value<string>("displayName");
if (!string.IsNullOrEmpty(displayName))
{
context.Identity.AddClaim(new Claim(SquidexClaimTypes.SquidexDisplayName, displayName));
}
var id = context.User.Value<string>("id");
if (!string.IsNullOrEmpty(id))
{
var pictureUrl = $"https://apis.live.net/v5.0/{id}/picture";
context.Identity.AddClaim(new Claim(SquidexClaimTypes.SquidexPictureUrl, pictureUrl));
}
return base.CreatingTicket(context);
}
}
}

39
src/Squidex/Config/Identity/MicrosoftIdentityUsage.cs

@ -0,0 +1,39 @@
// ==========================================================================
// MicrosoftIdentityUsage.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
// ReSharper disable InvertIf
namespace Squidex.Config.Identity
{
public static class MicrosoftIdentityUsage
{
public static IApplicationBuilder UseMyMicrosoftAuthentication(this IApplicationBuilder app)
{
var options = app.ApplicationServices.GetService<IOptions<MyIdentityOptions>>().Value;
if (options.IsMicrosoftAuthConfigured())
{
var googleOptions =
new MicrosoftAccountOptions
{
ClientId = options.MicrosoftClient,
ClientSecret = options.MicrosoftSecret,
Events = new MicrosoftHandler()
};
app.UseMicrosoftAccountAuthentication(googleOptions);
}
return app;
}
}
}

11
src/Squidex/Config/Identity/MyIdentityOptions.cs

@ -22,10 +22,16 @@ namespace Squidex.Config.Identity
public string GithubSecret { get; set; }
public string MicrosoftClient { get; set; }
public string MicrosoftSecret { get; set; }
public bool EnforceAdmin { get; set; }
public bool RequiresHttps { get; set; }
public bool AllowPasswordAuth { get; set; }
public bool LockAutomatically { get; set; }
public bool IsAdminConfigured()
@ -42,5 +48,10 @@ namespace Squidex.Config.Identity
{
return !string.IsNullOrWhiteSpace(GoogleClient) && !string.IsNullOrWhiteSpace(GoogleSecret);
}
public bool IsMicrosoftAuthConfigured()
{
return !string.IsNullOrWhiteSpace(MicrosoftClient) && !string.IsNullOrWhiteSpace(MicrosoftSecret);
}
}
}

2
src/Squidex/Config/Swagger/SwaggerServices.cs

@ -87,7 +87,7 @@ namespace Squidex.Config.Swagger
settings.DocumentProcessors.Add(new XmlTagProcessor());
settings.OperationProcessors.Add(new XmlTagProcessor());
settings.OperationProcessors.Add(new XmlResponseTypesProcessor());
settings.OperationProcessors.Add(new XmlResponseTypesProcessor(settings));
return settings;
}

15
src/Squidex/Config/Swagger/XmlResponseTypesProcessor.cs

@ -15,6 +15,7 @@ using NJsonSchema.Infrastructure;
using NSwag;
using NSwag.SwaggerGeneration.Processors;
using NSwag.SwaggerGeneration.Processors.Contexts;
using NSwag.SwaggerGeneration.WebApi;
using Squidex.Controllers.Api;
// ReSharper disable UseObjectOrCollectionInitializer
@ -25,6 +26,13 @@ namespace Squidex.Config.Swagger
{
private static readonly Regex ResponseRegex = new Regex("(?<Code>[0-9]{3}) => (?<Description>.*)", RegexOptions.Compiled);
private readonly WebApiToSwaggerGeneratorSettings settings;
public XmlResponseTypesProcessor(WebApiToSwaggerGeneratorSettings settings)
{
this.settings = settings;
}
public async Task<bool> ProcessAsync(OperationProcessorContext context)
{
var hasOkResponse = false;
@ -62,7 +70,7 @@ namespace Squidex.Config.Swagger
return true;
}
private static async Task AddInternalErrorResponseAsync(OperationProcessorContext context, SwaggerOperation operation)
private async Task AddInternalErrorResponseAsync(OperationProcessorContext context, SwaggerOperation operation)
{
if (operation.Responses.ContainsKey("500"))
{
@ -70,11 +78,12 @@ namespace Squidex.Config.Swagger
}
var errorType = typeof(ErrorDto);
var errorSchema = JsonObjectTypeDescription.FromType(errorType, new Attribute[0], EnumHandling.String);
var errorContract = settings.ActualContractResolver.ResolveContract(errorType);
var errorScheme = JsonObjectTypeDescription.FromType(errorType, errorContract, new Attribute[0], EnumHandling.String);
var response = new SwaggerResponse { Description = "Operation failed." };
response.Schema = await context.SwaggerGenerator.GenerateAndAppendSchemaFromTypeAsync(errorType, errorSchema.IsNullable, null);
response.Schema = await context.SwaggerGenerator.GenerateAndAppendSchemaFromTypeAsync(errorType, errorScheme.IsNullable, null);
operation.Responses.Add("500", response);
}

25
src/Squidex/Controllers/Api/Users/Models/CreateUserDto.cs

@ -0,0 +1,25 @@
// ==========================================================================
// CreateUserDto.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.Users.Models
{
public sealed class CreateUserDto
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
public string DisplayName { get; set; }
[Required]
public string Password { get; set; }
}
}

24
src/Squidex/Controllers/Api/Users/Models/UpdateUserDto.cs

@ -0,0 +1,24 @@
// ==========================================================================
// UpdateUserDto.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.Users.Models
{
public sealed class UpdateUserDto
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
public string DisplayName { get; set; }
public string Password { get; set; }
}
}

22
src/Squidex/Controllers/Api/Users/UserManagementController.cs

@ -51,6 +51,28 @@ namespace Squidex.Controllers.Api.Users
return Ok(model);
}
[HttpPost]
[Route("user-management")]
[ApiCosts(0)]
public async Task<IActionResult> Create([FromBody] CreateUserDto request)
{
var id = await userRepository.CreateAsync(request.Email, request.DisplayName, request.Password);
var model = new EntityCreatedDto { Id = id };
return Ok(model);
}
[HttpPut]
[Route("user-management/{id}")]
[ApiCosts(0)]
public async Task<IActionResult> Update(string id, [FromBody] UpdateUserDto request)
{
await userRepository.UpdateAsync(id, request.Email, request.DisplayName, request.Password);
return NoContent();
}
[HttpPut]
[Route("user-management/{id}/lock/")]
[ApiCosts(0)]

6
src/Squidex/Controllers/ContentApi/Generator/SchemasSwaggerGenerator.cs

@ -34,6 +34,7 @@ namespace Squidex.Controllers.ContentApi.Generator
{
public sealed class SchemasSwaggerGenerator
{
private readonly SwaggerOwinSettings swaggerSettings;
private readonly SwaggerJsonSchemaGenerator schemaGenerator;
private readonly SwaggerDocument document = new SwaggerDocument { Tags = new List<SwaggerTag>() };
private readonly HttpContext context;
@ -59,6 +60,8 @@ namespace Squidex.Controllers.ContentApi.Generator
schemaBodyDescription = SwaggerHelper.LoadDocs("schemabody");
schemaQueryDescription = SwaggerHelper.LoadDocs("schemaquery");
this.swaggerSettings = swaggerSettings;
}
public async Task<SwaggerDocument> Generate(IAppEntity targetApp, IEnumerable<ISchemaEntity> schemas)
@ -130,7 +133,8 @@ namespace Squidex.Controllers.ContentApi.Generator
private async Task GenerateBasicSchemas()
{
var errorType = typeof(ErrorDto);
var errorSchema = JsonObjectTypeDescription.FromType(errorType, new Attribute[0], EnumHandling.String);
var errorContract = swaggerSettings.ActualContractResolver.ResolveContract(errorType);
var errorSchema = JsonObjectTypeDescription.FromType(errorType, errorContract, new Attribute[0], EnumHandling.String);
errorDtoSchema = await swaggerGenerator.GenerateAndAppendSchemaFromTypeAsync(errorType, errorSchema.IsNullable, null);
}

66
src/Squidex/Controllers/UI/Account/AccountController.cs

@ -21,6 +21,7 @@ using Microsoft.Extensions.Options;
using Squidex.Config;
using Squidex.Config.Identity;
using Squidex.Core.Identity;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Tasks;
@ -125,15 +126,64 @@ namespace Squidex.Controllers.UI.Account
return RedirectToAction(nameof(LogoutCompleted));
}
[HttpGet]
[Route("account/signup/")]
public IActionResult Signup(string returnUrl = null)
{
return LoginView(returnUrl, false, false);
}
[HttpGet]
[Route("account/login/")]
public IActionResult Login(string returnUrl = null)
{
var providers =
return LoginView(returnUrl, true, false);
}
[HttpPost]
[Route("account/login/")]
public async Task<IActionResult> Login(LoginModel model, string returnUrl = null)
{
if (!ModelState.IsValid)
{
return LoginView(returnUrl, true, true);
}
var result = await signInManager.PasswordSignInAsync(model.Email, model.Password, true, true);
if (!result.Succeeded)
{
return LoginView(returnUrl, true, true);
}
else if (!string.IsNullOrWhiteSpace(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return Redirect("~/../");
}
}
private IActionResult LoginView(string returnUrl, bool isLogin, bool isFailed)
{
var allowPasswordAuth = identityOptions.Value.AllowPasswordAuth;
var providers =
signInManager.GetExternalAuthenticationSchemes()
.Select(x => new ExternalProvider(x.AuthenticationScheme, x.DisplayName)).ToList();
return View(new LoginVM { ExternalProviders = providers, ReturnUrl = returnUrl });
var vm = new LoginVM
{
ExternalProviders = providers,
IsLogin = isLogin,
IsFailed = isFailed,
HasPasswordAuth = allowPasswordAuth,
HasPasswordAndExternal = allowPasswordAuth && providers.Any(),
ReturnUrl = returnUrl
};
return View("Login", vm);
}
[HttpPost]
@ -204,7 +254,7 @@ namespace Squidex.Controllers.UI.Account
}
else
{
return Redirect("~/");
return Redirect("~/../");
}
}
@ -249,6 +299,16 @@ namespace Squidex.Controllers.UI.Account
{
var user = new IdentityUser { Email = email, UserName = email };
if (!externalLogin.Principal.HasClaim(x => x.Type == SquidexClaimTypes.SquidexPictureUrl))
{
user.AddClaim(new Claim(SquidexClaimTypes.SquidexPictureUrl, GravatarHelper.CreatePictureUrl(email)));
}
if (!externalLogin.Principal.HasClaim(x => x.Type == SquidexClaimTypes.SquidexDisplayName))
{
user.AddClaim(new Claim(SquidexClaimTypes.SquidexDisplayName, email));
}
foreach (var squidexClaim in externalLogin.Principal.Claims.Where(c => c.Type.StartsWith(SquidexClaimTypes.Prefix)))
{
user.AddClaim(squidexClaim);

21
src/Squidex/Controllers/UI/Account/LoginModel.cs

@ -0,0 +1,21 @@
// ==========================================================================
// LoginModel.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.UI.Account
{
public sealed class LoginModel
{
[Required]
public string Email { get; set; }
[Required]
public string Password { get; set; }
}
}

8
src/Squidex/Controllers/UI/Account/LoginVM.cs

@ -14,6 +14,14 @@ namespace Squidex.Controllers.UI.Account
{
public string ReturnUrl { get; set; }
public bool IsLogin { get; set; }
public bool IsFailed { get; set; }
public bool HasPasswordAuth { get; set; }
public bool HasPasswordAndExternal { get; set; }
public IEnumerable<ExternalProvider> ExternalProviders { get; set; }
}
}

47
src/Squidex/Squidex.csproj

@ -39,36 +39,37 @@
<ItemGroup>
<PackageReference Include="AspNet.Security.OAuth.GitHub" Version="1.0.0-rc1-final" />
<PackageReference Include="Autofac" Version="4.5.0" />
<PackageReference Include="Autofac" Version="4.6.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.1.0" />
<PackageReference Include="IdentityServer4" Version="1.5.0" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="1.2.0" />
<PackageReference Include="IdentityServer4" Version="1.5.1" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="1.2.1" />
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="1.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="0.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.HttpOverrides" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="0.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.HttpOverrides" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration.Tools" Version="1.1.0-preview4-final" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.2" />
<PackageReference Include="MongoDB.Driver" Version="2.4.3" />
<PackageReference Include="NJsonSchema" Version="8.34.6331.29178" />
<PackageReference Include="NJsonSchema" Version="9.1.2" />
<PackageReference Include="NodaTime.Serialization.JsonNet" Version="2.0.0" />
<PackageReference Include="NSwag.AspNetCore" Version="10.6.0" />
<PackageReference Include="OpenCover" Version="4.6.519" />
<PackageReference Include="ReportGenerator" Version="2.5.6" />
<PackageReference Include="StackExchange.Redis.StrongName" Version="1.2.1" />
<PackageReference Include="ReportGenerator" Version="2.5.8" />
<PackageReference Include="StackExchange.Redis.StrongName" Version="1.2.3" />
<PackageReference Include="System.Linq" Version="4.3.0" />
</ItemGroup>

1
src/Squidex/Startup.cs

@ -144,6 +144,7 @@ namespace Squidex
identityApp.UseMyApiProtection();
identityApp.UseMyGoogleAuthentication();
identityApp.UseMyGithubAuthentication();
identityApp.UseMyMicrosoftAuthentication();
identityApp.UseStaticFiles();
identityApp.MapWhen(x => IsIdentityRequest(x), mvcApp =>

109
src/Squidex/Views/Account/Login.cshtml

@ -4,25 +4,106 @@
@model Squidex.Controllers.UI.Account.LoginVM
@{
ViewBag.Title = "Login";
var type = Model.IsLogin ? "Login" : "Signup";
ViewBag.Title = type;
}
<form asp-controller="Account" asp-action="External" asp-route-returnurl="@Model.ReturnUrl" method="post">
<div>
<p>
@foreach (var provider in Model.ExternalProviders)
<div class="login">
<img class="login-logo" src="~/images/logo-small.png" />
<div class="row">
<div class="col-6">
@if (Model.IsLogin)
{
<button class="redirect-button" type="submit" name="provider" value="@provider.AuthenticationScheme" title="Log in using your @provider.DisplayName account">@provider.AuthenticationScheme</button>
<a class="login-headline active" asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl">Login</a>
}
</p>
else
{
<a class="login-headline" asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl">Login</a>
}
</div>
<div class="col-6 login-headline">
@if (!Model.IsLogin)
{
<a class="login-headline active" asp-controller="Account" asp-action="Signup" asp-route-returnurl="@Model.ReturnUrl">Signup</a>
}
else
{
<a class="login-headline" asp-controller="Account" asp-action="Signup" asp-route-returnurl="@Model.ReturnUrl">Signup</a>
}
</div>
</div>
</form>
<script>
var redirectButtons = document.getElementsByClassName("redirect-button");
<form asp-controller="Account" asp-action="External" asp-route-returnurl="@Model.ReturnUrl" method="post">
@foreach (var provider in Model.ExternalProviders)
{
var schema = provider.AuthenticationScheme.ToLowerInvariant();
<div class="form-group">
<button class="btn btn-external btn-block btn btn-@schema" type="submit" name="provider" value="@provider.AuthenticationScheme">
<i class="icon-@schema"></i> @type with <strong>@provider.AuthenticationScheme</strong>
</button>
</div>
}
</form>
@if (Model.HasPasswordAndExternal)
{
<div class="login-separator">
<div class="login-separator-text">OR</div>
</div>
}
@if (@Model.HasPasswordAuth)
{
@if (Model.IsLogin)
{
@if (Model.IsFailed)
{
<div class="form-error">Email or password not correct.</div>
}
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl" method="post">
<div class="form-group">
<input type="email" class="form-control" name="email" id="email" placeholder="Enter Email">
</div>
<div class="form-group">
<input type="password" class="form-control" name="password" id="password" placeholder="Enter Password">
</div>
<button type="submit" class="btn btn-block btn-primary">@type</button>
</form>
}
else
{
<div class="login-password-signup text-center">Ask your administrator to create an account.</div>
}
}
if (redirectButtons.length === 1) {
debugger;
redirectButtons[0].click();
@if (Model.IsLogin)
{
<p class="login-footer">
No account yet? <a asp-controller="Account" asp-action="Signup" asp-route-returnurl="@Model.ReturnUrl">Click here to signup</a>
</p>
}
else
{
<p class="login-footer">
Already registered? <a asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl">Click here to login</a>
</p>
}
</script>
</div>
@if (!Model.HasPasswordAuth && Model.ExternalProviders.Count() == 1)
{
<script>
var redirectButtons = document.getElementsByClassName("redirect-button");
if (redirectButtons.length === 1) {
debugger;
redirectButtons[0].click();
}
</script>
}

14
src/Squidex/app/theme/_bootstrap.scss

@ -1,6 +1,8 @@
@import '_mixins';
@import '_vars';
@import './../../node_modules/bootstrap/scss/mixins/_buttons';
body {
background: $color-background;
}
@ -167,6 +169,18 @@ body {
}
.btn {
&-github {
@include button-variant($color-dark-foreground, $color-extern-github, $color-extern-github);
}
&-google {
@include button-variant($color-dark-foreground, $color-extern-google, $color-extern-google);
}
&-microsoft {
@include button-variant($color-dark-foreground, $color-extern-microsoft, $color-extern-microsoft);
}
&-cancel {
padding: .4rem;
font-size: 1.1rem;

89
src/Squidex/app/theme/_static.scss

@ -1,6 +1,87 @@
@import '_mixins';
@import '_vars';
noscript {
display: block;
color: $color-theme-error;
font-size: 30px;
font-weight: lighter;
margin-bottom: 20px;
}
.login {
& {
max-width: 20rem;
margin: 0 auto;
padding: 1rem 2rem;
}
&-headline {
& {
color: $color-empty;
display: block;
margin-bottom: 1rem;
font-size: 1.3rem;
font-weight: normal;
text-align: center;
text-transform: uppercase;
}
&.active {
color: $color-text;
cursor: pointer;
pointer-events: none;
}
}
&-logo {
@include fixed(1rem, auto, auto, 1rem);
}
&-password-signup {
color: $color-empty;
}
&-footer {
font-size: .8rem;
font-weight: normal;
margin-top: 2rem;
}
&-separator {
& {
text-align: center;
margin-bottom: 2.5rem;
margin-top: 1.5rem;
border-bottom: 1px solid $color-border;
}
&-text {
color: darken($color-border, 15%);
display: inline-block;
background: $color-background;
border: 0;
bottom: -.7rem;
position: relative;
padding: 0 1rem;
}
}
.icon-microsoft {
&::before {
color: $color-dark-foreground;
}
}
.btn-external {
text-align: left;
}
.form-group {
margin-bottom: .5rem;
}
}
.splash {
&-h1,
&-text {
@ -19,12 +100,4 @@
font-size: 30px;
font-weight: lighter;
}
}
noscript {
display: block;
color: $color-theme-error;
font-size: 30px;
font-weight: lighter;
margin-bottom: 20px;
}

4
src/Squidex/app/theme/_vars.scss

@ -12,6 +12,10 @@ $color-control: rgba(0, 0, 0, .15);
$color-input: #dbe4eb;
$color-disabled: #eef1f4;
$color-extern-google: #d34836;
$color-extern-microsoft: #0063b1;
$color-extern-github: #3c4146;
$color-theme-blue: #438cef;
$color-theme-blue-dark: #3d7dd5;
$color-theme-blue-light: #9ebeea;

10
src/Squidex/app/theme/icomoon/demo-files/demo.css

@ -147,19 +147,19 @@ p {
font-size: 16px;
}
.fs1 {
font-size: 24px;
font-size: 32px;
}
.fs2 {
font-size: 28px;
font-size: 32px;
}
.fs3 {
font-size: 20px;
font-size: 24px;
}
.fs4 {
font-size: 32px;
font-size: 28px;
}
.fs5 {
font-size: 32px;
font-size: 20px;
}
.fs6 {
font-size: 32px;

1018
src/Squidex/app/theme/icomoon/demo.html

File diff suppressed because it is too large

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.eot

Binary file not shown.

3
src/Squidex/app/theme/icomoon/fonts/icomoon.svg

@ -70,4 +70,7 @@
<glyph unicode="&#xe93c;" glyph-name="info" d="M636.518 972.8c68.608 0 102.912-46.694 102.912-100.198 0-66.816-59.597-128.614-137.165-128.614-64.973 0-102.861 38.4-101.069 101.888 0 53.402 45.107 126.925 135.322 126.925zM425.421-51.2c-54.17 0-93.85 33.382-55.962 180.429l62.157 260.71c10.803 41.677 12.595 58.419 0 58.419-16.23 0-86.477-28.774-128.102-57.19l-27.034 45.056c131.686 111.923 283.187 177.51 348.211 177.51 54.118 0 63.13-65.178 36.096-165.376l-71.219-274.022c-12.595-48.384-7.219-65.075 5.427-65.075 16.23 0 69.478 20.070 121.805 61.798l30.72-41.677c-128.102-130.406-268.032-180.582-322.099-180.582z" />
<glyph unicode="&#xe93d;" glyph-name="bug" horiz-adv-x="951" d="M932.571 402.286c0-20-16.571-36.571-36.571-36.571h-128c0-71.429-15.429-125.143-38.286-165.714l118.857-119.429c14.286-14.286 14.286-37.143 0-51.429-6.857-7.429-16.571-10.857-25.714-10.857s-18.857 3.429-25.714 10.857l-113.143 112.571s-74.857-68.571-172-68.571v512h-73.143v-512c-103.429 0-178.857 75.429-178.857 75.429l-104.571-118.286c-7.429-8-17.143-12-27.429-12-8.571 0-17.143 2.857-24.571 9.143-14.857 13.714-16 36.571-2.857 52l115.429 129.714c-20 39.429-33.143 90.286-33.143 156.571h-128c-20 0-36.571 16.571-36.571 36.571s16.571 36.571 36.571 36.571h128v168l-98.857 98.857c-14.286 14.286-14.286 37.143 0 51.429s37.143 14.286 51.429 0l98.857-98.857h482.286l98.857 98.857c14.286 14.286 37.143 14.286 51.429 0s14.286-37.143 0-51.429l-98.857-98.857v-168h128c20 0 36.571-16.571 36.571-36.571zM658.286 731.428h-365.714c0 101.143 81.714 182.857 182.857 182.857s182.857-81.714 182.857-182.857z" />
<glyph unicode="&#xe93e;" glyph-name="download" d="M640 853.334q78 0 149.167-30.5t122.5-81.833 81.833-122.5 30.5-149.167q0-85-35-160.667t-96.667-129.167-140-77.5l21 20.667q18 18.333 28 42.667 9.333 22.667 9.333 49.333 0 6.667-0.333 9.333 59.333 41.333 93.833 105.833t34.5 139.5q0 60.667-23.667 116t-63.667 95.333-95.333 63.667-116 23.667q-55.333 0-106.5-19.833t-90-53.833-65-81.333-33.833-101h-88.667q-70.667 0-120.667-50t-50-120.667q0-38.667 15.167-71.667t39.833-54.167 54.833-33 60.833-11.833h50q11.667-29.333 30-48l37.667-37.333h-117.667q-69.667 0-128.5 34.333t-93.167 93.167-34.333 128.5 34.333 128.5 93.167 93.167 128.5 34.333h22q26.333 74.333 79.333 132.167t126.833 90.833 155.833 33zM554.667 512q17.667 0 30.167-12.5t12.5-30.167v-281l55 55.333q12.333 12.333 30.333 12.333 18.333 0 30.5-12.167t12.167-30.5q0-18-12.333-30.333l-128-128q-12.333-12.333-30.333-12.333t-30.333 12.333l-128 128q-12.333 13-12.333 30.333 0 17.667 12.5 30.167t30.167 12.5q18 0 30.333-12.333l55-55.333v281q0 17.667 12.5 30.167t30.167 12.5z" />
<glyph unicode="&#xe93f;" glyph-name="brand2" d="M522.24 521.152v-175.552h290.368c-11.712-75.328-87.68-220.8-290.368-220.8-174.72 0-317.44 144.64-317.44 323.2s142.72 323.2 317.44 323.2c99.456 0 166.016-42.24 204.16-78.912l138.88 133.888c-89.216 83.264-204.8 133.824-343.040 133.824-283.072 0-512-228.928-512-512s228.928-512 512-512c295.488 0 491.52 207.744 491.52 500.288 0 33.664-3.648 59.264-8.064 84.864h-483.456z" />
<glyph unicode="&#xe940;" glyph-name="microsoft" d="M0.35 448l-0.35 312.074 384 52.144v-364.218zM448 821.518l511.872 74.482v-448h-511.872zM959.998 384l-0.126-448-511.872 72.016v375.984zM384 16.164l-383.688 52.594-0.020 315.242h383.708z" />
<glyph unicode="&#xe941;" glyph-name="github" d="M512 960c-282.88 0-512-229.248-512-512 0-226.24 146.688-418.112 350.080-485.76 25.6-4.8 35.008 11.008 35.008 24.64 0 12.16-0.448 44.352-0.64 87.040-142.464-30.912-172.48 68.672-172.48 68.672-23.296 59.136-56.96 74.88-56.96 74.88-46.4 31.744 3.584 31.104 3.584 31.104 51.392-3.584 78.4-52.736 78.4-52.736 45.696-78.272 119.872-55.68 149.12-42.56 4.608 33.088 17.792 55.68 32.448 68.48-113.728 12.8-233.216 56.832-233.216 252.992 0 55.872 19.84 101.568 52.672 137.408-5.76 12.928-23.040 64.96 4.48 135.488 0 0 42.88 13.76 140.8-52.48 40.96 11.392 84.48 17.024 128 17.28 43.52-0.256 87.040-5.888 128-17.28 97.28 66.24 140.16 52.48 140.16 52.48 27.52-70.528 10.24-122.56 5.12-135.488 32.64-35.84 52.48-81.536 52.48-137.408 0-196.672-119.68-240-233.6-252.608 17.92-15.36 34.56-46.72 34.56-94.72 0-68.48-0.64-123.52-0.64-140.16 0-13.44 8.96-29.44 35.2-24.32 204.864 67.136 351.424 259.136 351.424 485.056 0 282.752-229.248 512-512 512z" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 56 KiB

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.ttf

Binary file not shown.

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.woff

Binary file not shown.

1451
src/Squidex/app/theme/icomoon/selection.json

File diff suppressed because it is too large

215
src/Squidex/app/theme/icomoon/style.css

@ -1,10 +1,10 @@
@font-face {
font-family: 'icomoon';
src: url('fonts/icomoon.eot?98f7w9');
src: url('fonts/icomoon.eot?98f7w9#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?98f7w9') format('truetype'),
url('fonts/icomoon.woff?98f7w9') format('woff'),
url('fonts/icomoon.svg?98f7w9#icomoon') format('svg');
src: url('fonts/icomoon.eot?h28uzm');
src: url('fonts/icomoon.eot?h28uzm#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?h28uzm') format('truetype'),
url('fonts/icomoon.woff?h28uzm') format('woff'),
url('fonts/icomoon.svg?h28uzm#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
@ -24,83 +24,12 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-download:before {
content: "\e93e";
}
.icon-control-RichText:before {
content: "\e939";
}
.icon-bug:before {
content: "\e93d";
}
.icon-control-Markdown:before {
content: "\e938";
}
.icon-control-Date:before {
content: "\e936";
}
.icon-control-DateTime:before {
content: "\e937";
}
.icon-angle-right:before {
content: "\e931";
}
.icon-user-o:before {
content: "\e932";
}
.icon-caret-right:before {
content: "\e929";
.icon-brand2:before {
content: "\e93f";
color: #4285f4;
}
.icon-caret-left:before {
content: "\e92a";
}
.icon-caret-up:before {
content: "\e92b";
}
.icon-caret-down:before {
content: "\e92c";
}
.icon-angle-up:before {
content: "\e903";
}
.icon-angle-down:before {
content: "\e900";
}
.icon-angle-left:before {
content: "\e901";
}
.icon-info:before {
content: "\e93c";
}
.icon-google:before {
content: "\e93b";
}
.icon-unlocked:before {
content: "\e933";
}
.icon-lock:before {
content: "\e934";
}
.icon-reset:before {
content: "\e92e";
}
.icon-pause:before {
content: "\e92f";
}
.icon-play:before {
content: "\e930";
}
.icon-settings2:before {
content: "\e92d";
}
.icon-bin2:before {
content: "\e902";
}
.icon-control-Stars:before {
content: "\e93a";
}
.icon-browser:before {
content: "\e935";
.icon-github:before {
content: "\e941";
}
.icon-activity:before {
content: "\e904";
@ -240,4 +169,126 @@
.icon-user:before {
content: "\e928";
}
.icon-microsoft:before {
content: "\e940";
}
.icon-google:before {
content: "\e93b";
}
.icon-unlocked:before {
content: "\e933";
}
.icon-lock:before {
content: "\e934";
}
.icon-reset:before {
content: "\e92e";
}
.icon-pause:before {
content: "\e92f";
}
.icon-play:before {
content: "\e930";
}
.icon-settings2:before {
content: "\e92d";
}
.icon-bin2:before {
content: "\e902";
}
.icon-download:before {
content: "\e93e";
}
.icon-control-RichText:before {
content: "\e939";
}
.icon-control-Date:before {
content: "\e936";
}
.icon-control-DateTime:before {
content: "\e937";
}
.icon-angle-right:before {
content: "\e931";
}
.icon-user-o:before {
content: "\e932";
}
.icon-caret-right:before {
content: "\e929";
}
.icon-caret-left:before {
content: "\e92a";
}
.icon-caret-up:before {
content: "\e92b";
}
.icon-bug:before {
content: "\e93d";
}
.icon-control-Markdown:before {
content: "\e938";
}
.icon-control-Date:before {
content: "\e936";
}
.icon-control-DateTime:before {
content: "\e937";
}
.icon-angle-right:before {
content: "\e931";
}
.icon-user-o:before {
content: "\e932";
}
.icon-caret-right:before {
content: "\e929";
}
.icon-caret-left:before {
content: "\e92a";
}
.icon-caret-up:before {
content: "\e92b";
}
.icon-caret-down:before {
content: "\e92c";
}
.icon-angle-up:before {
content: "\e903";
}
.icon-angle-down:before {
content: "\e900";
}
.icon-angle-left:before {
content: "\e901";
}
.icon-info:before {
content: "\e93c";
}
.icon-unlocked:before {
content: "\e933";
}
.icon-lock:before {
content: "\e934";
}
.icon-reset:before {
content: "\e92e";
}
.icon-pause:before {
content: "\e92f";
}
.icon-play:before {
content: "\e930";
}
.icon-settings2:before {
content: "\e92d";
}
.icon-bin2:before {
content: "\e902";
}
.icon-control-Stars:before {
content: "\e93a";
}
.icon-browser:before {
content: "\e935";
}

3
src/Squidex/appsettings.json

@ -46,10 +46,13 @@
}
},
"identity": {
"allowPasswordAuth": true,
"googleClient": "1006817248705-t3lb3ge808m9am4t7upqth79hulk456l.apps.googleusercontent.com",
"googleSecret": "QsEi-fHqkGw2_PjJmtNHf2wg",
"githubClient": "211ea00e726baf754c78",
"githubSecret": "d0a0d0fe2c26469ae20987ac265b3a339fd73132",
"microsoftClient": "b55da740-6648-4502-8746-b9003f29d5f1",
"microsoftSecret": "idWbANxNYEF4cB368WXJhjN",
"lockAutomatically": true,
"keysStore": {
"type": "InMemory",

2
tests/RunCoverage.ps1

@ -50,6 +50,6 @@ New-Item -ItemType directory -Path $reportsFolder
-output:"$workingFolder\$reportsFolder\Read.xml" `
-oldStyle
&"$userProfile\.nuget\packages\ReportGenerator\2.5.6\tools\ReportGenerator.exe" `
&"$userProfile\.nuget\packages\ReportGenerator\2.5.8\tools\ReportGenerator.exe" `
-reports:"$workingFolder\$reportsFolder\*.xml" `
-targetdir:"$workingFolder\$reportsFolder\Output"

2
tests/Squidex.Core.Tests/Squidex.Core.Tests.csproj

@ -12,7 +12,7 @@
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="4.19.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="Moq" Version="4.7.9" />
<PackageReference Include="Moq" Version="4.7.10" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
</ItemGroup>

6
tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj

@ -9,10 +9,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="4.19.2" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="Moq" Version="4.7.9" />
<PackageReference Include="Moq" Version="4.7.10" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
</ItemGroup>

2
tests/Squidex.Read.Tests/Squidex.Read.Tests.csproj

@ -15,7 +15,7 @@
<PackageReference Include="FluentAssertions" Version="4.19.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="MongoDB.Driver" Version="2.4.3" />
<PackageReference Include="Moq" Version="4.7.9" />
<PackageReference Include="Moq" Version="4.7.10" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
</ItemGroup>

2
tests/Squidex.Write.Tests/Squidex.Write.Tests.csproj

@ -14,7 +14,7 @@
<PackageReference Include="FluentAssertions" Version="4.19.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="MongoDB.Driver" Version="2.4.3" />
<PackageReference Include="Moq" Version="4.7.9" />
<PackageReference Include="Moq" Version="4.7.10" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
</ItemGroup>

Loading…
Cancel
Save