mirror of
https://github.com/bitwarden/server.git
synced 2025-04-06 21:48:12 -05:00
[EC-758] Add environment variable to enforce SSO Policy for all users (#2428)
* [EC-758] Add environment variable GlobalSettings.Sso.EnforceSsoPolicyForAllUsers to enforce SSO Policy for all users * [EC-758] Add integration tests * [EC-758] Add Entities namespace to resolve ambiguous reference * [EC-758] dotnet format * [EC-758] Updated integration tests to check for logins with all user types * [EC-758] Create new TestServer for each test * [EC-758] Combine unit tests and refactor to use BitAutoData
This commit is contained in:
parent
d60746d9b1
commit
4adc4b0181
@ -491,6 +491,7 @@ public class GlobalSettings : IGlobalSettings
|
|||||||
{
|
{
|
||||||
public int CacheLifetimeInSeconds { get; set; } = 60;
|
public int CacheLifetimeInSeconds { get; set; } = 60;
|
||||||
public double SsoTokenLifetimeInSeconds { get; set; } = 5;
|
public double SsoTokenLifetimeInSeconds { get; set; } = 5;
|
||||||
|
public bool EnforceSsoPolicyForAllUsers { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CaptchaSettings
|
public class CaptchaSettings
|
||||||
|
@ -4,4 +4,5 @@ public interface ISsoSettings
|
|||||||
{
|
{
|
||||||
int CacheLifetimeInSeconds { get; set; }
|
int CacheLifetimeInSeconds { get; set; }
|
||||||
double SsoTokenLifetimeInSeconds { get; set; }
|
double SsoTokenLifetimeInSeconds { get; set; }
|
||||||
|
bool EnforceSsoPolicyForAllUsers { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -368,7 +368,7 @@ public abstract class BaseRequestValidator<T> where T : class
|
|||||||
PolicyType.RequireSso);
|
PolicyType.RequireSso);
|
||||||
// Owners and Admins are exempt from this policy
|
// Owners and Admins are exempt from this policy
|
||||||
if (orgPolicy != null && orgPolicy.Enabled &&
|
if (orgPolicy != null && orgPolicy.Enabled &&
|
||||||
userOrg.Type != OrganizationUserType.Owner && userOrg.Type != OrganizationUserType.Admin)
|
(_globalSettings.Sso.EnforceSsoPolicyForAllUsers || (userOrg.Type != OrganizationUserType.Owner && userOrg.Type != OrganizationUserType.Admin)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Bit.Core.Entities;
|
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Api.Request.Accounts;
|
using Bit.Core.Models.Api.Request.Accounts;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
@ -7,6 +6,7 @@ using Bit.Identity.IdentityServer;
|
|||||||
using Bit.IntegrationTestCommon.Factories;
|
using Bit.IntegrationTestCommon.Factories;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using Bit.Test.Common.Helpers;
|
using Bit.Test.Common.Helpers;
|
||||||
|
using Microsoft.AspNetCore.TestHost;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@ -42,10 +42,9 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
|||||||
AssertHelper.AssertEqualJson(endpointRoot, knownConfigurationRoot);
|
AssertHelper.AssertEqualJson(endpointRoot, knownConfigurationRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory, BitAutoData]
|
||||||
public async Task TokenEndpoint_GrantTypePassword_Success()
|
public async Task TokenEndpoint_GrantTypePassword_Success(string deviceId)
|
||||||
{
|
{
|
||||||
var deviceId = "92b9d953-b9b6-4eaf-9d3e-11d57144dfeb";
|
|
||||||
var username = "test+tokenpassword@email.com";
|
var username = "test+tokenpassword@email.com";
|
||||||
|
|
||||||
await _factory.RegisterAsync(new RegisterRequestModel
|
await _factory.RegisterAsync(new RegisterRequestModel
|
||||||
@ -54,17 +53,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
|||||||
MasterPasswordHash = "master_password_hash"
|
MasterPasswordHash = "master_password_hash"
|
||||||
});
|
});
|
||||||
|
|
||||||
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
|
var context = await PostLoginAsync(_factory.Server, username, deviceId, context => context.SetAuthEmail(username));
|
||||||
{
|
|
||||||
{ "scope", "api offline_access" },
|
|
||||||
{ "client_id", "web" },
|
|
||||||
{ "deviceType", DeviceTypeAsString(DeviceType.FirefoxBrowser) },
|
|
||||||
{ "deviceIdentifier", deviceId },
|
|
||||||
{ "deviceName", "firefox" },
|
|
||||||
{ "grant_type", "password" },
|
|
||||||
{ "username", username },
|
|
||||||
{ "password", "master_password_hash" },
|
|
||||||
}), context => context.SetAuthEmail(username));
|
|
||||||
|
|
||||||
using var body = await AssertDefaultTokenBodyAsync(context);
|
using var body = await AssertDefaultTokenBodyAsync(context);
|
||||||
var root = body.RootElement;
|
var root = body.RootElement;
|
||||||
@ -77,10 +66,9 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
|||||||
Assert.Equal(5000, kdfIterations);
|
Assert.Equal(5000, kdfIterations);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory, BitAutoData]
|
||||||
public async Task TokenEndpoint_GrantTypePassword_NoAuthEmailHeader_Fails()
|
public async Task TokenEndpoint_GrantTypePassword_NoAuthEmailHeader_Fails(string deviceId)
|
||||||
{
|
{
|
||||||
var deviceId = "92b9d953-b9b6-4eaf-9d3e-11d57144dfeb";
|
|
||||||
var username = "test+noauthemailheader@email.com";
|
var username = "test+noauthemailheader@email.com";
|
||||||
|
|
||||||
await _factory.RegisterAsync(new RegisterRequestModel
|
await _factory.RegisterAsync(new RegisterRequestModel
|
||||||
@ -89,100 +77,204 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
|||||||
MasterPasswordHash = "master_password_hash",
|
MasterPasswordHash = "master_password_hash",
|
||||||
});
|
});
|
||||||
|
|
||||||
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
|
var context = await PostLoginAsync(_factory.Server, username, deviceId, null);
|
||||||
|
|
||||||
|
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
|
||||||
|
|
||||||
|
var body = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
|
||||||
|
var root = body.RootElement;
|
||||||
|
|
||||||
|
var error = AssertHelper.AssertJsonProperty(root, "error", JsonValueKind.String).GetString();
|
||||||
|
Assert.Equal("invalid_grant", error);
|
||||||
|
AssertHelper.AssertJsonProperty(root, "error_description", JsonValueKind.String);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task TokenEndpoint_GrantTypePassword_InvalidBase64AuthEmailHeader_Fails(string deviceId)
|
||||||
{
|
{
|
||||||
{ "scope", "api offline_access" },
|
var username = "test+badauthheader@email.com";
|
||||||
{ "client_id", "web" },
|
|
||||||
{ "deviceType", DeviceTypeAsString(DeviceType.FirefoxBrowser) },
|
await _factory.RegisterAsync(new RegisterRequestModel
|
||||||
{ "deviceIdentifier", deviceId },
|
{
|
||||||
{ "deviceName", "firefox" },
|
Email = username,
|
||||||
{ "grant_type", "password" },
|
MasterPasswordHash = "master_password_hash",
|
||||||
{ "username", username },
|
});
|
||||||
{ "password", "master_password_hash" },
|
|
||||||
|
var context = await PostLoginAsync(_factory.Server, username, deviceId, context => context.Request.Headers.Add("Auth-Email", "bad_value"));
|
||||||
|
|
||||||
|
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
|
||||||
|
|
||||||
|
var body = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
|
||||||
|
var root = body.RootElement;
|
||||||
|
|
||||||
|
var error = AssertHelper.AssertJsonProperty(root, "error", JsonValueKind.String).GetString();
|
||||||
|
Assert.Equal("invalid_grant", error);
|
||||||
|
AssertHelper.AssertJsonProperty(root, "error_description", JsonValueKind.String);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task TokenEndpoint_GrantTypePassword_WrongAuthEmailHeader_Fails(string deviceId)
|
||||||
|
{
|
||||||
|
var username = "test+badauthheader@email.com";
|
||||||
|
|
||||||
|
await _factory.RegisterAsync(new RegisterRequestModel
|
||||||
|
{
|
||||||
|
Email = username,
|
||||||
|
MasterPasswordHash = "master_password_hash",
|
||||||
|
});
|
||||||
|
|
||||||
|
var context = await PostLoginAsync(_factory.Server, username, deviceId, context => context.SetAuthEmail("bad_value"));
|
||||||
|
|
||||||
|
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
|
||||||
|
|
||||||
|
var body = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
|
||||||
|
var root = body.RootElement;
|
||||||
|
|
||||||
|
var error = AssertHelper.AssertJsonProperty(root, "error", JsonValueKind.String).GetString();
|
||||||
|
Assert.Equal("invalid_grant", error);
|
||||||
|
AssertHelper.AssertJsonProperty(root, "error_description", JsonValueKind.String);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(OrganizationUserType.Owner)]
|
||||||
|
[BitAutoData(OrganizationUserType.Admin)]
|
||||||
|
[BitAutoData(OrganizationUserType.User)]
|
||||||
|
[BitAutoData(OrganizationUserType.Manager)]
|
||||||
|
[BitAutoData(OrganizationUserType.Custom)]
|
||||||
|
public async Task TokenEndpoint_GrantTypePassword_WithAllUserTypes_WithSsoPolicyDisabled_WithEnforceSsoPolicyForAllUsersTrue_Success(OrganizationUserType organizationUserType, Guid organizationId, string deviceId, int generatedUsername)
|
||||||
|
{
|
||||||
|
var username = $"{generatedUsername}@example.com";
|
||||||
|
|
||||||
|
var server = _factory.WithWebHostBuilder(builder =>
|
||||||
|
{
|
||||||
|
builder.UseSetting("globalSettings:sso:enforceSsoPolicyForAllUsers", "true");
|
||||||
|
}).Server;
|
||||||
|
|
||||||
|
await server.PostAsync("/accounts/register", JsonContent.Create(new RegisterRequestModel
|
||||||
|
{
|
||||||
|
Email = username,
|
||||||
|
MasterPasswordHash = "master_password_hash"
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
|
await CreateOrganizationWithSsoPolicyAsync(organizationId, username, organizationUserType, ssoPolicyEnabled: false);
|
||||||
|
|
||||||
var body = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
|
var context = await PostLoginAsync(server, username, deviceId, context => context.SetAuthEmail(username));
|
||||||
var root = body.RootElement;
|
|
||||||
|
|
||||||
var error = AssertHelper.AssertJsonProperty(root, "error", JsonValueKind.String).GetString();
|
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
|
||||||
Assert.Equal("invalid_grant", error);
|
|
||||||
AssertHelper.AssertJsonProperty(root, "error_description", JsonValueKind.String);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task TokenEndpoint_GrantTypePassword_InvalidBase64AuthEmailHeader_Fails()
|
[BitAutoData(OrganizationUserType.Owner)]
|
||||||
|
[BitAutoData(OrganizationUserType.Admin)]
|
||||||
|
[BitAutoData(OrganizationUserType.User)]
|
||||||
|
[BitAutoData(OrganizationUserType.Manager)]
|
||||||
|
[BitAutoData(OrganizationUserType.Custom)]
|
||||||
|
public async Task TokenEndpoint_GrantTypePassword_WithAllUserTypes_WithSsoPolicyDisabled_WithEnforceSsoPolicyForAllUsersFalse_Success(OrganizationUserType organizationUserType, Guid organizationId, string deviceId, int generatedUsername)
|
||||||
{
|
{
|
||||||
var deviceId = "92b9d953-b9b6-4eaf-9d3e-11d57144dfeb";
|
var username = $"{generatedUsername}@example.com";
|
||||||
var username = "test+badauthheader@email.com";
|
|
||||||
|
|
||||||
await _factory.RegisterAsync(new RegisterRequestModel
|
var server = _factory.WithWebHostBuilder(builder =>
|
||||||
|
{
|
||||||
|
builder.UseSetting("globalSettings:sso:enforceSsoPolicyForAllUsers", "false");
|
||||||
|
}).Server;
|
||||||
|
|
||||||
|
await server.PostAsync("/accounts/register", JsonContent.Create(new RegisterRequestModel
|
||||||
{
|
{
|
||||||
Email = username,
|
Email = username,
|
||||||
MasterPasswordHash = "master_password_hash",
|
MasterPasswordHash = "master_password_hash"
|
||||||
});
|
}));
|
||||||
|
|
||||||
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
|
await CreateOrganizationWithSsoPolicyAsync(organizationId, username, organizationUserType, ssoPolicyEnabled: false);
|
||||||
{
|
|
||||||
{ "scope", "api offline_access" },
|
|
||||||
{ "client_id", "web" },
|
|
||||||
{ "deviceType", DeviceTypeAsString(DeviceType.FirefoxBrowser) },
|
|
||||||
{ "deviceIdentifier", deviceId },
|
|
||||||
{ "deviceName", "firefox" },
|
|
||||||
{ "grant_type", "password" },
|
|
||||||
{ "username", username },
|
|
||||||
{ "password", "master_password_hash" },
|
|
||||||
}), context => context.Request.Headers.Add("Auth-Email", "bad_value"));
|
|
||||||
|
|
||||||
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
|
var context = await PostLoginAsync(server, username, deviceId, context => context.SetAuthEmail(username));
|
||||||
|
|
||||||
var body = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
|
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
|
||||||
var root = body.RootElement;
|
|
||||||
|
|
||||||
var error = AssertHelper.AssertJsonProperty(root, "error", JsonValueKind.String).GetString();
|
|
||||||
Assert.Equal("invalid_grant", error);
|
|
||||||
AssertHelper.AssertJsonProperty(root, "error_description", JsonValueKind.String);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task TokenEndpoint_GrantTypePassword_WrongAuthEmailHeader_Fails()
|
[BitAutoData(OrganizationUserType.Owner)]
|
||||||
|
[BitAutoData(OrganizationUserType.Admin)]
|
||||||
|
[BitAutoData(OrganizationUserType.User)]
|
||||||
|
[BitAutoData(OrganizationUserType.Manager)]
|
||||||
|
[BitAutoData(OrganizationUserType.Custom)]
|
||||||
|
public async Task TokenEndpoint_GrantTypePassword_WithAllUserTypes_WithSsoPolicyEnabled_WithEnforceSsoPolicyForAllUsersTrue_Throw(OrganizationUserType organizationUserType, Guid organizationId, string deviceId, int generatedUsername)
|
||||||
{
|
{
|
||||||
var deviceId = "92b9d953-b9b6-4eaf-9d3e-11d57144dfeb";
|
var username = $"{generatedUsername}@example.com";
|
||||||
var username = "test+badauthheader@email.com";
|
|
||||||
|
|
||||||
await _factory.RegisterAsync(new RegisterRequestModel
|
var server = _factory.WithWebHostBuilder(builder =>
|
||||||
|
{
|
||||||
|
builder.UseSetting("globalSettings:sso:enforceSsoPolicyForAllUsers", "true");
|
||||||
|
}).Server;
|
||||||
|
|
||||||
|
await server.PostAsync("/accounts/register", JsonContent.Create(new RegisterRequestModel
|
||||||
{
|
{
|
||||||
Email = username,
|
Email = username,
|
||||||
MasterPasswordHash = "master_password_hash",
|
MasterPasswordHash = "master_password_hash"
|
||||||
});
|
}));
|
||||||
|
|
||||||
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
|
await CreateOrganizationWithSsoPolicyAsync(organizationId, username, organizationUserType, ssoPolicyEnabled: true);
|
||||||
{
|
|
||||||
{ "scope", "api offline_access" },
|
var context = await PostLoginAsync(server, username, deviceId, context => context.SetAuthEmail(username));
|
||||||
{ "client_id", "web" },
|
|
||||||
{ "deviceType", DeviceTypeAsString(DeviceType.FirefoxBrowser) },
|
|
||||||
{ "deviceIdentifier", deviceId },
|
|
||||||
{ "deviceName", "firefox" },
|
|
||||||
{ "grant_type", "password" },
|
|
||||||
{ "username", username },
|
|
||||||
{ "password", "master_password_hash" },
|
|
||||||
}), context => context.SetAuthEmail("bad_value"));
|
|
||||||
|
|
||||||
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
|
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
|
||||||
|
await AssertRequiredSsoAuthenticationResponseAsync(context);
|
||||||
var body = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
|
|
||||||
var root = body.RootElement;
|
|
||||||
|
|
||||||
var error = AssertHelper.AssertJsonProperty(root, "error", JsonValueKind.String).GetString();
|
|
||||||
Assert.Equal("invalid_grant", error);
|
|
||||||
AssertHelper.AssertJsonProperty(root, "error_description", JsonValueKind.String);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task TokenEndpoint_GrantTypeRefreshToken_Success()
|
[BitAutoData(OrganizationUserType.Owner)]
|
||||||
|
[BitAutoData(OrganizationUserType.Admin)]
|
||||||
|
public async Task TokenEndpoint_GrantTypePassword_WithOwnerOrAdmin_WithSsoPolicyEnabled_WithEnforceSsoPolicyForAllUsersFalse_Success(OrganizationUserType organizationUserType, Guid organizationId, string deviceId, int generatedUsername)
|
||||||
|
{
|
||||||
|
var username = $"{generatedUsername}@example.com";
|
||||||
|
|
||||||
|
var server = _factory.WithWebHostBuilder(builder =>
|
||||||
|
{
|
||||||
|
builder.UseSetting("globalSettings:sso:enforceSsoPolicyForAllUsers", "false");
|
||||||
|
}).Server;
|
||||||
|
|
||||||
|
await server.PostAsync("/accounts/register", JsonContent.Create(new RegisterRequestModel
|
||||||
|
{
|
||||||
|
Email = username,
|
||||||
|
MasterPasswordHash = "master_password_hash"
|
||||||
|
}));
|
||||||
|
|
||||||
|
await CreateOrganizationWithSsoPolicyAsync(organizationId, username, organizationUserType, ssoPolicyEnabled: true);
|
||||||
|
|
||||||
|
var context = await PostLoginAsync(server, username, deviceId, context => context.SetAuthEmail(username));
|
||||||
|
|
||||||
|
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(OrganizationUserType.User)]
|
||||||
|
[BitAutoData(OrganizationUserType.Manager)]
|
||||||
|
[BitAutoData(OrganizationUserType.Custom)]
|
||||||
|
public async Task TokenEndpoint_GrantTypePassword_WithNonOwnerOrAdmin_WithSsoPolicyEnabled_WithEnforceSsoPolicyForAllUsersFalse_Throws(OrganizationUserType organizationUserType, Guid organizationId, string deviceId, int generatedUsername)
|
||||||
|
{
|
||||||
|
var username = $"{generatedUsername}@example.com";
|
||||||
|
|
||||||
|
var server = _factory.WithWebHostBuilder(builder =>
|
||||||
|
{
|
||||||
|
builder.UseSetting("globalSettings:sso:enforceSsoPolicyForAllUsers", "false");
|
||||||
|
}).Server;
|
||||||
|
|
||||||
|
await server.PostAsync("/accounts/register", JsonContent.Create(new RegisterRequestModel
|
||||||
|
{
|
||||||
|
Email = username,
|
||||||
|
MasterPasswordHash = "master_password_hash"
|
||||||
|
}));
|
||||||
|
|
||||||
|
await CreateOrganizationWithSsoPolicyAsync(organizationId, username, organizationUserType, ssoPolicyEnabled: true);
|
||||||
|
|
||||||
|
var context = await PostLoginAsync(server, username, deviceId, context => context.SetAuthEmail(username));
|
||||||
|
|
||||||
|
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
|
||||||
|
await AssertRequiredSsoAuthenticationResponseAsync(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task TokenEndpoint_GrantTypeRefreshToken_Success(string deviceId)
|
||||||
{
|
{
|
||||||
var deviceId = "5a7b19df-0c9d-46bf-a104-8034b5a17182";
|
|
||||||
var username = "test+tokenrefresh@email.com";
|
var username = "test+tokenrefresh@email.com";
|
||||||
|
|
||||||
await _factory.RegisterAsync(new RegisterRequestModel
|
await _factory.RegisterAsync(new RegisterRequestModel
|
||||||
@ -204,11 +296,10 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
|||||||
AssertRefreshTokenExists(body.RootElement);
|
AssertRefreshTokenExists(body.RootElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory, BitAutoData]
|
||||||
public async Task TokenEndpoint_GrantTypeClientCredentials_Success()
|
public async Task TokenEndpoint_GrantTypeClientCredentials_Success(string deviceId)
|
||||||
{
|
{
|
||||||
var username = "test+tokenclientcredentials@email.com";
|
var username = "test+tokenclientcredentials@email.com";
|
||||||
var deviceId = "8f14a393-edfe-40ba-8c67-a856cb89c509";
|
|
||||||
|
|
||||||
await _factory.RegisterAsync(new RegisterRequestModel
|
await _factory.RegisterAsync(new RegisterRequestModel
|
||||||
{
|
{
|
||||||
@ -235,7 +326,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task TokenEndpoint_GrantTypeClientCredentials_AsOrganization_Success(Organization organization, OrganizationApiKey organizationApiKey)
|
public async Task TokenEndpoint_GrantTypeClientCredentials_AsOrganization_Success(Bit.Core.Entities.Organization organization, Bit.Core.Entities.OrganizationApiKey organizationApiKey)
|
||||||
{
|
{
|
||||||
var orgRepo = _factory.Services.GetRequiredService<IOrganizationRepository>();
|
var orgRepo = _factory.Services.GetRequiredService<IOrganizationRepository>();
|
||||||
organization.Enabled = true;
|
organization.Enabled = true;
|
||||||
@ -322,7 +413,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task TokenEndpoint_GrantTypeClientCredentials_AsInstallation_InstallationExists_Succeeds(Installation installation)
|
public async Task TokenEndpoint_GrantTypeClientCredentials_AsInstallation_InstallationExists_Succeeds(Bit.Core.Entities.Installation installation)
|
||||||
{
|
{
|
||||||
var installationRepo = _factory.Services.GetRequiredService<IInstallationRepository>();
|
var installationRepo = _factory.Services.GetRequiredService<IInstallationRepository>();
|
||||||
installation = await installationRepo.CreateAsync(installation);
|
installation = await installationRepo.CreateAsync(installation);
|
||||||
@ -394,14 +485,13 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
|||||||
Assert.Equal("invalid_client", error);
|
Assert.Equal("invalid_client", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory, BitAutoData]
|
||||||
public async Task TokenEndpoint_ToQuickInOneSecond_BlockRequest()
|
public async Task TokenEndpoint_ToQuickInOneSecond_BlockRequest(string deviceId)
|
||||||
{
|
{
|
||||||
const int AmountInOneSecondAllowed = 5;
|
const int AmountInOneSecondAllowed = 5;
|
||||||
|
|
||||||
// The rule we are testing is 10 requests in 1 second
|
// The rule we are testing is 10 requests in 1 second
|
||||||
var username = "test+ratelimiting@email.com";
|
var username = "test+ratelimiting@email.com";
|
||||||
var deviceId = "8f14a393-edfe-40ba-8c67-a856cb89c509";
|
|
||||||
|
|
||||||
await _factory.RegisterAsync(new RegisterRequestModel
|
await _factory.RegisterAsync(new RegisterRequestModel
|
||||||
{
|
{
|
||||||
@ -442,6 +532,72 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<HttpContext> PostLoginAsync(TestServer server, string username, string deviceId, Action<HttpContext> extraConfiguration)
|
||||||
|
{
|
||||||
|
return await server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "scope", "api offline_access" },
|
||||||
|
{ "client_id", "web" },
|
||||||
|
{ "deviceType", DeviceTypeAsString(DeviceType.FirefoxBrowser) },
|
||||||
|
{ "deviceIdentifier", deviceId },
|
||||||
|
{ "deviceName", "firefox" },
|
||||||
|
{ "grant_type", "password" },
|
||||||
|
{ "username", username },
|
||||||
|
{ "password", "master_password_hash" },
|
||||||
|
}), extraConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CreateOrganizationWithSsoPolicyAsync(Guid organizationId, string username, OrganizationUserType organizationUserType, bool ssoPolicyEnabled)
|
||||||
|
{
|
||||||
|
var userRepository = _factory.Services.GetService<IUserRepository>();
|
||||||
|
var organizationRepository = _factory.Services.GetService<IOrganizationRepository>();
|
||||||
|
var organizationUserRepository = _factory.Services.GetService<IOrganizationUserRepository>();
|
||||||
|
var policyRepository = _factory.Services.GetService<IPolicyRepository>();
|
||||||
|
|
||||||
|
var organization = await organizationRepository.GetByIdAsync(organizationId);
|
||||||
|
if (organization == null)
|
||||||
|
{
|
||||||
|
organization = new Bit.Core.Entities.Organization { Id = organizationId, Enabled = true, UseSso = ssoPolicyEnabled };
|
||||||
|
await organizationRepository.CreateAsync(organization);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
organization.UseSso = ssoPolicyEnabled;
|
||||||
|
await organizationRepository.ReplaceAsync(organization);
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = await userRepository.GetByEmailAsync(username);
|
||||||
|
var organizationUser = await organizationUserRepository.GetByOrganizationEmailAsync(organization.Id, username);
|
||||||
|
if (organizationUser == null)
|
||||||
|
{
|
||||||
|
organizationUser = new Bit.Core.Entities.OrganizationUser
|
||||||
|
{
|
||||||
|
OrganizationId = organization.Id,
|
||||||
|
UserId = user.Id,
|
||||||
|
Status = OrganizationUserStatusType.Confirmed,
|
||||||
|
Type = organizationUserType
|
||||||
|
};
|
||||||
|
await organizationUserRepository.CreateAsync(organizationUser);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
organizationUser.Type = organizationUserType;
|
||||||
|
await organizationUserRepository.ReplaceAsync(organizationUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ssoPolicy = await policyRepository.GetByOrganizationIdTypeAsync(organization.Id, PolicyType.RequireSso);
|
||||||
|
if (ssoPolicy == null)
|
||||||
|
{
|
||||||
|
ssoPolicy = new Bit.Core.Entities.Policy { OrganizationId = organization.Id, Type = PolicyType.RequireSso, Enabled = ssoPolicyEnabled };
|
||||||
|
await policyRepository.CreateAsync(ssoPolicy);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ssoPolicy.Enabled = ssoPolicyEnabled;
|
||||||
|
await policyRepository.ReplaceAsync(ssoPolicy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static string DeviceTypeAsString(DeviceType deviceType)
|
private static string DeviceTypeAsString(DeviceType deviceType)
|
||||||
{
|
{
|
||||||
return ((int)deviceType).ToString();
|
return ((int)deviceType).ToString();
|
||||||
@ -493,4 +649,15 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
|||||||
var actualScope = AssertScopeExists(tokenResponse);
|
var actualScope = AssertScopeExists(tokenResponse);
|
||||||
Assert.Equal(expectedScope, actualScope);
|
Assert.Equal(expectedScope, actualScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task AssertRequiredSsoAuthenticationResponseAsync(HttpContext context)
|
||||||
|
{
|
||||||
|
var body = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
|
||||||
|
var root = body.RootElement;
|
||||||
|
|
||||||
|
var error = AssertHelper.AssertJsonProperty(root, "error", JsonValueKind.String).GetString();
|
||||||
|
Assert.Equal("invalid_grant", error);
|
||||||
|
var errorDescription = AssertHelper.AssertJsonProperty(root, "error_description", JsonValueKind.String).GetString();
|
||||||
|
Assert.StartsWith("sso authentication", errorDescription.ToLowerInvariant());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user