Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Publish EntraCP v25.0 #252

Closed
wants to merge 12 commits into from
Closed
2 changes: 1 addition & 1 deletion .github/workflows/publish-nightly-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Publish nightly release
on: workflow_dispatch
jobs:
call-workflow-publish-nightly-release:
uses: Yvand/AzureCP/.github/workflows/reusable-build-publish-release.yml@master
uses: Yvand/EntraCP/.github/workflows/reusable-build-publish-release.yml@master
with:
project-name: ${{ vars.PROJECT_NAME }}
version-major-minor: ${{ vars.VERSION_MAJOR_MINOR }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-production-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Publish production release
on: workflow_dispatch
jobs:
call-workflow-publish-nightly-release:
uses: Yvand/AzureCP/.github/workflows/reusable-build-publish-release.yml@master
uses: Yvand/EntraCP/.github/workflows/reusable-build-publish-release.yml@master
with:
project-name: ${{ vars.PROJECT_NAME }}
version-major-minor: ${{ vars.VERSION_MAJOR_MINOR }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ on:

jobs:
call-workflow-prepare-dtl-env:
uses: Yvand/AzureCP/.github/workflows/reusable-prepare-dtl-env.yml@master
uses: Yvand/EntraCP/.github/workflows/reusable-prepare-dtl-env.yml@master
with:
project-name: ${{ vars.PROJECT_NAME }}
sharepoint-versions: ${{ inputs.sharepoint_versions }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/verify-prs-and-commits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:

jobs:
call-build:
uses: Yvand/AzureCP/.github/workflows/reusable-build.yml@master
uses: Yvand/EntraCP/.github/workflows/reusable-build.yml@master
with:
project-name: ${{ vars.PROJECT_NAME }}
version-major-minor: ${{ vars.VERSION_MAJOR_MINOR }}
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Change log for ~~AzureCP~~ EntraCP

## Unreleased

* Update the global configuration page to give the possibility to update the credentials of tenants already registered - https://github.com/Yvand/EntraCP/pull/248
* Fix an issue where tenant credentials could not be changed from a client certificate to a client secret - https://github.com/Yvand/EntraCP/pull/248
* Prompt user for confirmation before actually deleting a tenant - https://github.com/Yvand/EntraCP/pull/248
* New feature: It is now possible to configure EntraCP, to return only users that are members of some Entra groups, configured by the administrator - https://github.com/Yvand/EntraCP/pull/243
* Fix the connection to Microsoft Graph not working when the tenant is hosted in a national cloud
* Update Azure.Identity from 1.10.4 to 1.11.2
* Update Microsoft.Graph from 5.44.0 to 5.49.0

## EntraCP v24.0.20240318.32 enhancements & bug-fixes - Published in March 18, 2024

* Fix "Edit" links fail to work on the "Claim types configuration" page - https://github.com/Yvand/EntraCP/pull/220
Expand Down
20 changes: 14 additions & 6 deletions Yvand.EntraCP.Tests/BasicConfigurationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,39 +21,47 @@ public override void CheckSettingsTest()
}

[Test, TestCaseSource(typeof(EntraIdTestGroupsSource), nameof(EntraIdTestGroupsSource.GetTestData), new object[] { true })]
public void TestAllEntraIDGroups(EntraIdTestGroup group)
public void TestAllTestGroups(EntraIdTestGroup group)
{
TestSearchAndValidateForEntraIDGroup(group);
}

[Test]
public void TestSomeEntraIDGroups([Random(0, UnitTestsHelper.TotalNumberOfGroupsInSource - 1, 5)] int idx)
public void TestRandomTestGroups([Random(0, UnitTestsHelper.TestGroupsCount - 1, 5)] int idx)
{
EntraIdTestGroup group = EntraIdTestGroupsSource.Groups[idx];
TestSearchAndValidateForEntraIDGroup(group);
}

[Test, TestCaseSource(typeof(EntraIdTestUsersSource), nameof(EntraIdTestUsersSource.GetTestData), null)]
public void TestAllEntraIDUsers(EntraIdTestUser user)
public void TestAllTestUsers(EntraIdTestUser user)
{
base.TestSearchAndValidateForEntraIDUser(user);
}

[Test]
public void TestSomeEntraIDUsers([Random(0, UnitTestsHelper.TotalNumberOfUsersInSource - 1, 5)] int idx)
public void TestRandomTestUsers([Random(0, UnitTestsHelper.TestUsersCount - 1, 5)] int idx)
{
var user = EntraIdTestUsersSource.Users[idx];
base.TestSearchAndValidateForEntraIDUser(user);
}

[Test]
[Repeat(5)]
public override void TestAugmentationForUsersMembersOfAllGroups()
public override void TestAugmentationOfGoldUsersAgainstRandomGroups()
{
base.TestAugmentationForUsersMembersOfAllGroups();
base.TestAugmentationOfGoldUsersAgainstRandomGroups();
}

#if DEBUG
[TestCase("testEntraCPUser_001")]
[TestCase("testEntraCPUser_020")]
public void DebugTestUser(string upnPrefix)
{
EntraIdTestUser user = EntraIdTestUsersSource.Users.Find(x => x.UserPrincipalName.StartsWith(upnPrefix));
base.TestSearchAndValidateForEntraIDUser(user);
}

[TestCase(@"testentracp", 30, "")]
public void DebugSearchManual(string inputValue, int expectedResultCount, string expectedEntityClaimValue)
{
Expand Down
4 changes: 2 additions & 2 deletions Yvand.EntraCP.Tests/BypassDirectoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ public void TestAllEntraIDUsers(EntraIdTestUser user)

[Test]
[Repeat(5)]
public override void TestAugmentationForUsersMembersOfAllGroups()
public override void TestAugmentationOfGoldUsersAgainstRandomGroups()
{
base.TestAugmentationForUsersMembersOfAllGroups();
base.TestAugmentationOfGoldUsersAgainstRandomGroups();
}
}
}
126 changes: 103 additions & 23 deletions Yvand.EntraCP.Tests/ClaimsProviderTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Text;
Expand Down Expand Up @@ -48,6 +47,35 @@ public string GroupIdentifierClaimType

protected EntraIDProviderSettings Settings = new EntraIDProviderSettings();

private object _LockVerifyIfCurrentUserShouldBeFound = new object();
private object _LockInitGroupsWhichUsersMustBeMemberOfAny = new object();
private List<EntraIdTestGroupSettings> _GroupsWhichUsersMustBeMemberOfAny;
protected List<EntraIdTestGroupSettings> GroupsWhichUsersMustBeMemberOfAny
{
get
{
if (_GroupsWhichUsersMustBeMemberOfAny != null) { return _GroupsWhichUsersMustBeMemberOfAny; }
lock (_LockInitGroupsWhichUsersMustBeMemberOfAny)
{
if (_GroupsWhichUsersMustBeMemberOfAny != null) { return _GroupsWhichUsersMustBeMemberOfAny; }
_GroupsWhichUsersMustBeMemberOfAny = new List<EntraIdTestGroupSettings>();
string groupsWhichUsersMustBeMemberOfAny = Settings.RestrictSearchableUsersByGroups;
if (!String.IsNullOrWhiteSpace(groupsWhichUsersMustBeMemberOfAny))
{
string[] groupIds = groupsWhichUsersMustBeMemberOfAny.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string groupId in groupIds)
{
EntraIdTestGroupSettings groupSettings = EntraIdTestGroupsSource.GroupsSettings.FirstOrDefault(x => x.Id == groupId);
if (groupSettings == null) { groupSettings = new EntraIdTestGroupSettings(); }
_GroupsWhichUsersMustBeMemberOfAny.Add(groupSettings);
}
}
return _GroupsWhichUsersMustBeMemberOfAny;
}
}
}


/// <summary>
/// Initialize settings
/// </summary>
Expand All @@ -62,10 +90,8 @@ public virtual void InitializeSettings()
Settings.Timeout = 99999;
#endif

string json = File.ReadAllText(UnitTestsHelper.AzureTenantsJsonFile);
List<EntraIDTenant> azureTenants = JsonConvert.DeserializeObject<List<EntraIDTenant>>(json);
Settings.EntraIDTenants = azureTenants;
foreach (EntraIDTenant tenant in azureTenants)
Settings.EntraIDTenants = new List<EntraIDTenant> { UnitTestsHelper.TenantConnection };
foreach (EntraIDTenant tenant in Settings.EntraIDTenants)
{
tenant.ExcludeMemberUsers = ExcludeMemberUsers;
tenant.ExcludeGuestUsers = ExcludeGuestUsers;
Expand Down Expand Up @@ -126,37 +152,91 @@ public void TestSearchAndValidateForEntraIDUser(EntraIdTestUser entity)
}
else
{
if (entity.UserType == UserType.Member)
if (!String.IsNullOrWhiteSpace(Settings.RestrictSearchableUsersByGroups))
{
claimValue = Settings.ClaimTypes.UserIdentifierConfig.EntityProperty == Configuration.DirectoryObjectProperty.UserPrincipalName ?
entity.UserPrincipalName :
entity.Mail;
expectedCount = ExcludeMemberUsers ? 0 : 1;
shouldValidate = !ExcludeMemberUsers;
lock (_LockVerifyIfCurrentUserShouldBeFound) // TODO: understand why this lock is necessary
{
// Test 1: Does Settings.RestrictSearchableUsersByGroups contain any group where all test users are members?
bool groupWithAllTestUsersAreMembersFound = false;
foreach (var groupSettings in GroupsWhichUsersMustBeMemberOfAny)
{
if (groupSettings.AllTestUsersAreMembers)
{
groupWithAllTestUsersAreMembersFound = true;
Trace.TraceInformation($"{DateTime.Now:s} [{this.GetType().Name}] User \"{entity.UserPrincipalName}\" may be found because Settings.RestrictSearchableUsersByGroups contains group: \"{groupSettings.DisplayName}\" with AllTestUsersAreMembers {groupSettings.AllTestUsersAreMembers}.");
break; // No need to change shouldValidate, which is true by default, or process other groups
}
}

// Test 2: If test 1 is false, is current entity member of all the test groups?
if (!groupWithAllTestUsersAreMembersFound)
{

EntraIdTestUserSettings userSettings = EntraIdTestUsersSource.UsersWithSpecificSettings.FirstOrDefault(x => String.Equals(x.UserPrincipalName, entity.UserPrincipalName, StringComparison.InvariantCultureIgnoreCase));
if (userSettings == null) { userSettings = new EntraIdTestUserSettings(); }
if (!userSettings.IsMemberOfAllGroups)
{
shouldValidate = false;
expectedCount = 0;
Trace.TraceInformation($"{DateTime.Now:s} [{this.GetType().Name}] User \"{entity.UserPrincipalName}\" should not be found because it has IsMemberOfAllGroups {userSettings.IsMemberOfAllGroups} and no group set in Settings.RestrictSearchableUsersByGroups has AllTestUsersAreMembers set to true.");
}
}
}
}
else
{
claimValue = Settings.ClaimTypes.UserIdentifierConfig.DirectoryObjectPropertyForGuestUsers == Configuration.DirectoryObjectProperty.UserPrincipalName ?
entity.UserPrincipalName :
entity.Mail;
expectedCount = ExcludeGuestUsers ? 0 : 1;
shouldValidate = !ExcludeGuestUsers;
Trace.TraceInformation($"{DateTime.Now:s} [{this.GetType().Name}] Property Settings.RestrictSearchableUsersByGroups IsNullOrWhiteSpace.");
}

// If shouldValidate is false, user should not be found anyway so no need to do additional checks
if (shouldValidate)
{
if (entity.UserType == UserType.Member)
{
claimValue = Settings.ClaimTypes.UserIdentifierConfig.EntityProperty == Configuration.DirectoryObjectProperty.UserPrincipalName ?
entity.UserPrincipalName :
entity.Mail;
expectedCount = ExcludeMemberUsers ? 0 : 1;
shouldValidate = !ExcludeMemberUsers;
}
else
{
claimValue = Settings.ClaimTypes.UserIdentifierConfig.DirectoryObjectPropertyForGuestUsers == Configuration.DirectoryObjectProperty.UserPrincipalName ?
entity.UserPrincipalName :
entity.Mail;
expectedCount = ExcludeGuestUsers ? 0 : 1;
shouldValidate = !ExcludeGuestUsers;
}
}
}
TestSearchOperation(inputValue, expectedCount, claimValue);
TestValidationOperation(UserIdentifierClaimType, claimValue, shouldValidate);
}

public virtual void TestAugmentationForUsersMembersOfAllGroups()
/// <summary>
/// Gold users are the test users who are members of all the test groups
/// </summary>
public virtual void TestAugmentationOfGoldUsersAgainstRandomGroups()
{
Random rnd = new Random();
int randomIdx = rnd.Next(0, UnitTestsHelper.TotalNumberOfGroupsInSource - 1);
var anyGroup = EntraIdTestGroupsSource.Groups[randomIdx];
bool shouldBeMember = Settings.FilterSecurityEnabledGroupsOnly && !anyGroup.SecurityEnabled ? false : true;
foreach (string userAccountName in UnitTestsHelper.UsersMembersOfAllGroups)
int randomIdx = rnd.Next(0, UnitTestsHelper.TestGroupsCount - 1);
Trace.TraceInformation($"{DateTime.Now:s} [{this.GetType().Name}] TestAugmentationOfGoldUsersAgainstRandomGroups: Get group in EntraIdTestGroupsSource.Groups at index {randomIdx}.");
EntraIdTestGroup randomGroup = null;
try
{
randomGroup = EntraIdTestGroupsSource.Groups[randomIdx];
}
catch (ArgumentOutOfRangeException)
{
string errorMessage = $"{DateTime.Now:s} [{this.GetType().Name}] TestAugmentationOfGoldUsersAgainstRandomGroups: Could not get group in EntraIdTestGroupsSource.Groups at index {randomIdx}. EntraIdTestGroupsSource.Groups has {EntraIdTestGroupsSource.Groups.Count} items.";
Trace.TraceError(errorMessage);
throw new ArgumentOutOfRangeException(errorMessage);
}
bool shouldBeMember = Settings.FilterSecurityEnabledGroupsOnly && !randomGroup.SecurityEnabled ? false : true;

foreach (string userPrincipalName in EntraIdTestUsersSource.UsersWithSpecificSettings.Where(x => x.IsMemberOfAllGroups).Select(x => x.UserPrincipalName))
{
string userPrincipalName = EntraIdTestUsersSource.Users.First(x => x.UserPrincipalName.StartsWith(userAccountName, StringComparison.InvariantCultureIgnoreCase)).UserPrincipalName;
TestAugmentationOperation(userPrincipalName, shouldBeMember, anyGroup.Id);
TestAugmentationOperation(userPrincipalName, shouldBeMember, randomGroup.Id);
}
}

Expand Down
12 changes: 6 additions & 6 deletions Yvand.EntraCP.Tests/ExcludeAUserTypeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ public void TestAllEntraIDUsers(EntraIdTestUser user)

[Test]
[Repeat(5)]
public override void TestAugmentationForUsersMembersOfAllGroups()
public override void TestAugmentationOfGoldUsersAgainstRandomGroups()
{
base.TestAugmentationForUsersMembersOfAllGroups();
base.TestAugmentationOfGoldUsersAgainstRandomGroups();
}
}

Expand Down Expand Up @@ -74,9 +74,9 @@ public void TestAllEntraIDUsers(EntraIdTestUser user)

[Test]
[Repeat(5)]
public override void TestAugmentationForUsersMembersOfAllGroups()
public override void TestAugmentationOfGoldUsersAgainstRandomGroups()
{
base.TestAugmentationForUsersMembersOfAllGroups();
base.TestAugmentationOfGoldUsersAgainstRandomGroups();
}
}

Expand Down Expand Up @@ -113,9 +113,9 @@ public void TestAllEntraIDUsers(EntraIdTestUser user)

[Test]
[Repeat(5)]
public override void TestAugmentationForUsersMembersOfAllGroups()
public override void TestAugmentationOfGoldUsersAgainstRandomGroups()
{
base.TestAugmentationForUsersMembersOfAllGroups();
base.TestAugmentationOfGoldUsersAgainstRandomGroups();
}
}
}
Loading
Loading