diff --git a/.github/workflows/verify-prs-and-commits.yml b/.github/workflows/verify-prs-and-commits.yml new file mode 100644 index 00000000..7eeec5cd --- /dev/null +++ b/.github/workflows/verify-prs-and-commits.yml @@ -0,0 +1,17 @@ +name: Verify PRs and commits + +on: + workflow_dispatch: + push: + branches: [ "master", "dev" ] + pull_request: + branches: [ "master", "dev" ] + +jobs: + call-build: + uses: Yvand/AzureCP/.github/workflows/reusable-build.yml@master + with: + project-name: ${{ vars.PROJECT_NAME }} + version-major-minor: ${{ vars.VERSION_MAJOR_MINOR }} + secrets: + base64-encoded-signingkey: ${{ secrets.BASE64_ENCODED_SIGNINGKEY }} diff --git a/AzureCP.Tests/AzureCP.Tests.csproj b/AzureCP.Tests/AzureCP.Tests.csproj index 06f19a4b..5b5f61c5 100644 --- a/AzureCP.Tests/AzureCP.Tests.csproj +++ b/AzureCP.Tests/AzureCP.Tests.csproj @@ -74,13 +74,13 @@ 1.0.12 - 13.0.1 + 13.0.2 3.13.3 - 4.2.1 + 4.4.2 runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/AzureCP.Tests/EntityTestsBase.cs b/AzureCP.Tests/EntityTestsBase.cs index 61bb0aff..7ef4c070 100644 --- a/AzureCP.Tests/EntityTestsBase.cs +++ b/AzureCP.Tests/EntityTestsBase.cs @@ -114,9 +114,9 @@ public void DEBUG_SearchEntitiesFromCollection(string inputValue, string expecte UnitTestsHelper.TestSearchOperation(inputValue, Convert.ToInt32(expectedCount), expectedClaimValue); } - [TestCase(@"AADGroup1", 1, "5b0f6c56-c87f-44c3-9354-56cba03da433")] + [TestCase(@"AADGroup1", 1, "30ef0958-c003-4667-a0ad-ef9783acaf25")] [TestCase(@"xyzguest", 0, "xyzGUEST@contoso.com")] - [TestCase(@"AzureGr}", 1, "141cfd15-3941-4cbc-859f-d7125938fb72")] + [TestCase(@"AzureGr}", 1, "ef7d18e6-5c4d-451a-9663-a976be81c91e")] public void DEBUG_SearchEntities(string inputValue, int expectedResultCount, string expectedEntityClaimValue) { if (!TestSearch) { return; } diff --git a/AzureCP.Tests/UnitTestsHelper.cs b/AzureCP.Tests/UnitTestsHelper.cs index e24c44f7..47835179 100644 --- a/AzureCP.Tests/UnitTestsHelper.cs +++ b/AzureCP.Tests/UnitTestsHelper.cs @@ -27,7 +27,7 @@ public class UnitTestsHelper public const int MaxTime = 50000; public static readonly string FarmAdmin = TestContext.Parameters["FarmAdmin"]; #if DEBUG - public const int TestRepeatCount = 5; + public const int TestRepeatCount = 1; #else public const int TestRepeatCount = 20; #endif @@ -68,7 +68,7 @@ public static void InitializeSiteCollection() #if DEBUG TestSiteCollUri = new Uri("http://spsites/sites/" + TestContext.Parameters["TestSiteCollectionName"]); - return; // Uncommented when debugging AzureCP code from unit tests + //return; // Uncommented when debugging AzureCP code from unit tests #endif diff --git a/AzureCP.Tests/local.runsettings b/AzureCP.Tests/local.runsettings index 8fee1fc3..18b5d31c 100644 --- a/AzureCP.Tests/local.runsettings +++ b/AzureCP.Tests/local.runsettings @@ -4,15 +4,15 @@ x64 - - - - - + + + + + - + diff --git a/AzureCP.sln b/AzureCP.sln index c7496b71..ca725e22 100644 --- a/AzureCP.sln +++ b/AzureCP.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2020 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33110.190 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureCP", "AzureCP\AzureCP.csproj", "{EEC47949-34B5-4805-A04D-A372BE75D3CB}" EndProject diff --git a/AzureCP/AzureCP.cs b/AzureCP/AzureCP.cs index 161f66d8..23fa9ab2 100644 --- a/AzureCP/AzureCP.cs +++ b/AzureCP/AzureCP.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Net.Http; using System.Reflection; using System.Text; using System.Threading; @@ -401,9 +402,61 @@ public static SPTrustedLoginProvider GetSPTrustAssociatedWithCP(string providerI /// Null if property doesn't exist, String.Empty if property exists but has no value, actual value otherwise public static string GetPropertyValue(object directoryObject, string propertyName) { - if (directoryObject == null) { return null; } + if (directoryObject == null) + { + return null; + } + + if (propertyName.StartsWith("extensionAttribute")) + { + try + { + var returnString = string.Empty; + if (directoryObject is User) + { + var userobject = (Microsoft.Graph.User)directoryObject; + if (userobject.AdditionalData != null) + { + var obj = userobject.AdditionalData.FirstOrDefault(s => s.Key.EndsWith(propertyName)); + if (obj.Value != null) + { + returnString = obj.Value.ToString(); + } + else + { + return null; + } + } + } + else if (directoryObject is Group) + { + var groupobject = (Microsoft.Graph.Group)directoryObject; + if (groupobject.AdditionalData != null) + { + var obj = groupobject.AdditionalData.FirstOrDefault(s => s.Key.EndsWith(propertyName)); + if (obj.Value != null) + { + returnString = obj.Value.ToString(); + } + else + { + return null; + } + } + } + return returnString == null ? propertyName : returnString; + } + catch + { + return null; + } + } + PropertyInfo pi = directoryObject.GetType().GetProperty(propertyName); - if (pi == null) { return null; } // Property doesn't exist + if (pi == null) + { + return null; + } // Property doesn't exist object propertyValue = pi.GetValue(directoryObject, null); return propertyValue == null ? String.Empty : propertyValue.ToString(); } @@ -436,20 +489,27 @@ protected virtual PickerEntity CreatePickerEntityHelper(AzureCPResult result) isMappedClaimTypeConfig = true; } + entity.EntityType = result.ClaimTypeConfig.SharePointEntityType; if (result.ClaimTypeConfig.UseMainClaimTypeOfDirectoryObject) { string claimValueType; if (result.ClaimTypeConfig.EntityType == DirectoryObjectType.User) { permissionClaimType = IdentityClaimTypeConfig.ClaimType; - entity.EntityType = SPClaimEntityTypes.User; claimValueType = IdentityClaimTypeConfig.ClaimValueType; + if (String.IsNullOrEmpty(entity.EntityType)) + { + entity.EntityType = SPClaimEntityTypes.User; + } } else { permissionClaimType = MainGroupClaimTypeConfig.ClaimType; - entity.EntityType = ClaimsProviderConstants.GroupClaimEntityType; claimValueType = MainGroupClaimTypeConfig.ClaimValueType; + if (String.IsNullOrEmpty(entity.EntityType)) + { + entity.EntityType = ClaimsProviderConstants.GroupClaimEntityType; + } } permissionValue = FormatPermissionValue(permissionClaimType, permissionValue, isMappedClaimTypeConfig, result); claim = CreateClaim( @@ -464,7 +524,10 @@ protected virtual PickerEntity CreatePickerEntityHelper(AzureCPResult result) permissionClaimType, permissionValue, result.ClaimTypeConfig.ClaimValueType); - entity.EntityType = result.ClaimTypeConfig.EntityType == DirectoryObjectType.User ? SPClaimEntityTypes.User : ClaimsProviderConstants.GroupClaimEntityType; + if (String.IsNullOrEmpty(entity.EntityType)) + { + entity.EntityType = result.ClaimTypeConfig.EntityType == DirectoryObjectType.User ? SPClaimEntityTypes.User : ClaimsProviderConstants.GroupClaimEntityType; + } } entity.Claim = claim; @@ -476,16 +539,21 @@ protected virtual PickerEntity CreatePickerEntityHelper(AzureCPResult result) result.QueryMatchValue); int nbMetadata = 0; - // Populate metadata of new PickerEntity - foreach (ClaimTypeConfig ctConfig in MetadataConfig.Where(x => x.EntityType == result.ClaimTypeConfig.EntityType)) + // If current result is a SharePoint group but was found on an AAD User object, then 1 to many User objects could match so no metadata from the current match should be set + if (!String.Equals(result.ClaimTypeConfig.SharePointEntityType, ClaimsProviderConstants.GroupClaimEntityType, StringComparison.InvariantCultureIgnoreCase) || + result.ClaimTypeConfig.EntityType != DirectoryObjectType.User ) { - // if there is actally a value in the GraphObject, then it can be set - string entityAttribValue = GetPropertyValue(result.UserOrGroupResult, ctConfig.DirectoryObjectProperty.ToString()); - if (!String.IsNullOrEmpty(entityAttribValue)) + // Populate metadata of new PickerEntity + foreach (ClaimTypeConfig ctConfig in MetadataConfig.Where(x => x.EntityType == result.ClaimTypeConfig.EntityType)) { - entity.EntityData[ctConfig.EntityDataKey] = entityAttribValue; - nbMetadata++; - ClaimsProviderLogging.Log($"[{ProviderInternalName}] Set metadata '{ctConfig.EntityDataKey}' of new entity to '{entityAttribValue}'", TraceSeverity.VerboseEx, EventSeverity.Information, TraceCategory.Claims_Picking); + // if there is actally a value in the GraphObject, then it can be set + string entityAttribValue = GetPropertyValue(result.UserOrGroupResult, ctConfig.DirectoryObjectProperty.ToString()); + if (!String.IsNullOrEmpty(entityAttribValue)) + { + entity.EntityData[ctConfig.EntityDataKey] = entityAttribValue; + nbMetadata++; + ClaimsProviderLogging.Log($"[{ProviderInternalName}] Set metadata '{ctConfig.EntityDataKey}' of new entity to '{entityAttribValue}'", TraceSeverity.VerboseEx, EventSeverity.Information, TraceCategory.Claims_Picking); + } } } entity.DisplayText = FormatPermissionDisplayText(entity, isMappedClaimTypeConfig, result); @@ -571,7 +639,11 @@ protected virtual List CreatePickerEntityForSpecificClaimTypes(str PickerEntity entity = CreatePickerEntity(); entity.Claim = claim; entity.IsResolved = true; - entity.EntityType = ctConfig.EntityType == DirectoryObjectType.User ? SPClaimEntityTypes.User : ClaimsProviderConstants.GroupClaimEntityType; + entity.EntityType = ctConfig.SharePointEntityType; + if (String.IsNullOrEmpty(entity.EntityType)) + { + entity.EntityType = ctConfig.EntityType == DirectoryObjectType.User ? SPClaimEntityTypes.User : ClaimsProviderConstants.GroupClaimEntityType; + } //entity.EntityGroupName = ""; entity.Description = String.Format(PickerEntityOnMouseOver, ctConfig.DirectoryObjectProperty.ToString(), input); @@ -1187,6 +1259,12 @@ protected virtual void BuildFilter(OperationContext currentContext, List QueryAzureADTenantAsync(OperationCon // Initialize requests and variables that will receive the result IGraphServiceUsersCollectionPage usersFound = null; IGraphServiceGroupsCollectionPage groupsFound = null; - IGraphServiceUsersCollectionRequest userRequest = tenant.GraphService.Users.Request().Select(tenant.UserSelect).Filter(tenant.UserFilter).Top(currentContext.MaxCount); + + // Allow Advanced query as documented in https://learn.microsoft.com/en-us/graph/aad-advanced-queries?tabs=http : + // Add ConsistencyLevel header to eventual and $count=true to fix $filter on CompanyName - https://github.com/Yvand/AzureCP/issues/166 + // Only work for non-batched requests + List