Skip to content

Commit

Permalink
Publish v21 (#182)
Browse files Browse the repository at this point in the history
* Update AzureCP.csproj

* get fields from types ClaimTypeConfig, and IdentityClaimTypeConfig if needed (#171)

* Set advanced query (reopened to update dev branch) (#172)

* set advanced query

* Set ConsistencyLevel as a HeaderOption

* reference SharePoint dll from the project itself to avoid using GAC

reference SharePoint dll from the project itself to avoid using GAC

* Add support for extension attributes (#174)

* get fields from types ClaimTypeConfig, and IdentityClaimTypeConfig if needed

* set advanced query

* Set ConsistencyLevel as a HeaderOption

* Set advanced query (#168)

* Update AzureCP.csproj

* set advanced query

* Set ConsistencyLevel as a HeaderOption

* Fix idctype notupdated (#167)

* Update AzureCP.csproj

* get fields from types ClaimTypeConfig, and IdentityClaimTypeConfig if needed

* Revert "Fix idctype notupdated (#167)" (#169)

This reverts commit 918575a.

* Revert "Set advanced query (#168)" (#170)

This reverts commit 02c2603.

* Initial: add extension Attributes to dropdowns

* New field for AD Connect Client ID, Replacement of extensionAttribute property names in queries.

* Fix to return the guid without dashes

* Added persisted prop to certificate

* Fixed property handling for extension attributes

* Fixed issue with the display of extensionAttributes within the central admin config page.

* Minor fixes to get better results

* Update AzureCP.csproj

* Update AzureCP.cs

* rename ADConnectClientId to ExtensionAttributesApplicationId

* reference SharePoint dll from the project itself to avoid using GAC

reference SharePoint dll from the project itself to avoid using GAC

* Update AzureCP.cs

* Revert "Update AzureCP.cs"

This reverts commit c56256f.

* Update ClaimTypesConfig.ascx

* Update AzureCP.cs

* add property SharePointEntityType

* Added formsrole to FillEntityTypes

* Revert "Added formsrole to FillEntityTypes"

This reverts commit 7d2e33a.

* update tests

---------

Co-authored-by: Yvan Duhamel <[email protected]>

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Set header ConsistencyLevel eventual in batching (#177)

* set header ConsistencyLevel eventual in batching

* Update CHANGELOG.md

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* update workflow

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* Update build-solutions.yml

* replace workflow

* Update verify-prs-and-commits.yml

* Create publish-nightly-release.yml

* Update verify-prs-and-commits.yml

* Update import of  pfx X509Certificate2 object

* Update how certificate is loaded in memory

* remove explicit creation of file for X509Certificate2 constructor

* Update ci-run-integration-tests.yml for Azure Pipelines

* Update verify-prs-and-commits.yml

* Update verify-prs-and-commits.yml

* Update verify-prs-and-commits.yml

* Update CHANGELOG.md

* Update CHANGELOG.md

---------

Co-authored-by: Andi Krüger <[email protected]>
  • Loading branch information
Yvand and andikrueger authored Jul 3, 2023
1 parent b5d117b commit b0e36a0
Show file tree
Hide file tree
Showing 17 changed files with 328 additions and 59 deletions.
17 changes: 17 additions & 0 deletions .github/workflows/verify-prs-and-commits.yml
Original file line number Diff line number Diff line change
@@ -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 }}
4 changes: 2 additions & 2 deletions AzureCP.Tests/AzureCP.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@
<Version>1.0.12</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>13.0.1</Version>
<Version>13.0.2</Version>
</PackageReference>
<PackageReference Include="NUnit">
<Version>3.13.3</Version>
</PackageReference>
<PackageReference Include="NUnit3TestAdapter">
<Version>4.2.1</Version>
<Version>4.4.2</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
4 changes: 2 additions & 2 deletions AzureCP.Tests/EntityTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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, "[email protected]")]
[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; }
Expand Down
4 changes: 2 additions & 2 deletions AzureCP.Tests/UnitTestsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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


Expand Down
12 changes: 6 additions & 6 deletions AzureCP.Tests/local.runsettings
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
<TargetPlatform>x64</TargetPlatform>
</RunConfiguration>
<TestRunParameters>
<Parameter name="AzureTenantsJsonFile" value="C:\Data\Dev\AzureCP_Tenants.json" />
<Parameter name="DataFile_AllAccounts_Search" value="C:\Data\Dev\AzureCP_Tests_AllAccounts_Search.csv" />
<Parameter name="DataFile_AllAccounts_Validate" value="C:\Data\Dev\AzureCP_Tests_AllAccounts_Validate.csv" />
<Parameter name="DataFile_GuestAccountsUPN_Search" value="C:\Data\Dev\AzureCP_Tests_GuestAccountsUPN_Search.csv" />
<Parameter name="DataFile_GuestAccountsUPN_Validate" value="C:\Data\Dev\AzureCP_Tests_GuestAccountsUPN_Validate.csv" />
<Parameter name="AzureTenantsJsonFile" value="C:\YvanData\Dev\AzureCP_Tenants.json" />
<Parameter name="DataFile_AllAccounts_Search" value="C:\YvanData\Dev\AzureCP_Tests_AllAccounts_Search.csv" />
<Parameter name="DataFile_AllAccounts_Validate" value="C:\YvanData\Dev\AzureCP_Tests_AllAccounts_Validate.csv" />
<Parameter name="DataFile_GuestAccountsUPN_Search" value="C:\YvanData\Dev\AzureCP_Tests_GuestAccountsUPN_Search.csv" />
<Parameter name="DataFile_GuestAccountsUPN_Validate" value="C:\YvanData\Dev\AzureCP_Tests_GuestAccountsUPN_Validate.csv" />
<Parameter name="FarmAdmin" value="i:0#.w|contoso\yvand" />
<Parameter name="TestSiteCollectionName" value="AzureCP.UnitTests" />
<Parameter name="TrustedGroupToAdd_ClaimType" value="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" />
<Parameter name="TrustedGroupToAdd_ClaimValue" value="99abdc91-e6e0-475c-a0ba-5014f91de853" />
<Parameter name="TrustedGroupToAdd_ClaimValue" value="30ef0958-c003-4667-a0ad-ef9783acaf25" />
<Parameter name="ClaimsProviderConfigName" value="AzureCPConfig" />
<Parameter name="TestLogFileName" value="AzureCPIntegrationTests.log" />
</TestRunParameters>
Expand Down
4 changes: 2 additions & 2 deletions AzureCP.sln
Original file line number Diff line number Diff line change
@@ -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
Expand Down
144 changes: 123 additions & 21 deletions AzureCP/AzureCP.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -401,9 +402,61 @@ public static SPTrustedLoginProvider GetSPTrustAssociatedWithCP(string providerI
/// <returns>Null if property doesn't exist, String.Empty if property exists but has no value, actual value otherwise</returns>
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();
}
Expand Down Expand Up @@ -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(
Expand All @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -571,7 +639,11 @@ protected virtual List<PickerEntity> 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);

Expand Down Expand Up @@ -1187,6 +1259,12 @@ protected virtual void BuildFilter(OperationContext currentContext, List<AzureTe
foreach (ClaimTypeConfig ctConfig in currentContext.CurrentClaimTypeConfigList)
{
string currentPropertyString = ctConfig.DirectoryObjectProperty.ToString();

if (currentPropertyString.StartsWith("extensionAttribute"))
{
currentPropertyString = String.Format("{0}_{1}_{2}", "extension", "EXTENSIONATTRIBUTESAPPLICATIONID", currentPropertyString);
}

string currentFilter;
if (!ctConfig.SupportsWildcard)
{
Expand Down Expand Up @@ -1290,9 +1368,14 @@ protected virtual void BuildFilter(OperationContext currentContext, List<AzureTe

foreach (AzureTenant tenant in azureTenants)
{
string encodedUserFilterForTenant = encodedUserFilter.Replace("EXTENSIONATTRIBUTESAPPLICATIONID", tenant.ExtensionAttributesApplicationId.ToString("N"));
string encodedUserSelectForTenant = encodedUserSelect.Replace("EXTENSIONATTRIBUTESAPPLICATIONID", tenant.ExtensionAttributesApplicationId.ToString("N"));
string encodedGroupFilterForTenant = encodedGroupFilter.Replace("EXTENSIONATTRIBUTESAPPLICATIONID", tenant.ExtensionAttributesApplicationId.ToString("N"));
string encodedGroupSelectForTenant = encodedgroupSelect.Replace("EXTENSIONATTRIBUTESAPPLICATIONID", tenant.ExtensionAttributesApplicationId.ToString("N"));

if (firstUserObjectProcessed)
{
tenant.UserFilter = encodedUserFilter;
tenant.UserFilter = encodedUserFilterForTenant;
//if (tenant.MemberUserTypeOnly)
// tenant.UserFilter += encodedMemberOnlyUserTypeFilter;
//else if (tenant.ExcludeGuestUsers)
Expand All @@ -1306,15 +1389,15 @@ protected virtual void BuildFilter(OperationContext currentContext, List<AzureTe

if (firstGroupObjectProcessed)
{
tenant.GroupFilter = encodedGroupFilter;
tenant.GroupFilter = encodedGroupFilterForTenant;
}
else
{
tenant.GroupFilter = String.Empty;
}

tenant.UserSelect = encodedUserSelect;
tenant.GroupSelect = encodedgroupSelect;
tenant.UserSelect = encodedUserSelectForTenant;
tenant.GroupSelect = encodedGroupSelectForTenant;
}
}

Expand Down Expand Up @@ -1386,18 +1469,37 @@ protected virtual async Task<AzureADResult> 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<Option> nonBatchedUserQueryOptions = new List<Option>
{
new QueryOption("$count", "true"),
new HeaderOption("ConsistencyLevel", "eventual")
};
IGraphServiceUsersCollectionRequest userRequest = tenant.GraphService.Users.Request(nonBatchedUserQueryOptions).Select(tenant.UserSelect).Filter(tenant.UserFilter).Top(currentContext.MaxCount);
IGraphServiceGroupsCollectionRequest groupRequest = tenant.GraphService.Groups.Request().Select(tenant.GroupSelect).Filter(tenant.GroupFilter).Top(currentContext.MaxCount);
// Do a batch query only if necessary
if (!String.IsNullOrWhiteSpace(tenant.UserFilter) && !String.IsNullOrWhiteSpace(tenant.GroupFilter))
{
// Fix https://github.com/Yvand/AzureCP/issues/175: Create the BatchRequestStep manually to be able to set the HTTP header ConsistencyLevel: eventual
// Implememted as shown in https://learn.microsoft.com/en-us/graph/sdks/batch-requests?tabs=csharp#implementing-batching-using-batchrequestcontent-batchrequeststep-and-httprequestmessage
// How header "ConsistencyLevel" must be set in batching: https://learn.microsoft.com/en-us/graph/json-batching#first-json-batch-request
var httpUserRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"https://graph.microsoft.com/v1.0/users/?$count=true&$select={tenant.UserSelect}&$filter={tenant.UserFilter}");
httpUserRequestMessage.Content = new StringContent(String.Empty, Encoding.UTF8, "application/json"); // Create a fake content to be able to set the header below
httpUserRequestMessage.Content.Headers.Add("ConsistencyLevel", "eventual");
string userRequestId = Guid.NewGuid().ToString();
BatchRequestStep requestStep = new BatchRequestStep(userRequestId, httpUserRequestMessage);
// https://docs.microsoft.com/en-us/graph/sdks/batch-requests?tabs=csharp
BatchRequestContent batchRequestContent = new BatchRequestContent();
var userRequestId = batchRequestContent.AddBatchRequestStep(userRequest);
//var userRequestId = batchRequestContent.AddBatchRequestStep(userRequest); // Use httpUserRequestMessage instead
batchRequestContent.AddBatchRequestStep(requestStep);
var groupRequestId = batchRequestContent.AddBatchRequestStep(groupRequest);
var batchResponse = await tenant.GraphService.Batch.Request().PostAsync(batchRequestContent).ConfigureAwait(false);
// De - serialize batch response based on known return type
GraphServiceUsersCollectionResponse usersBatchResponse = await batchResponse.GetResponseByIdAsync<GraphServiceUsersCollectionResponse>(userRequestId).ConfigureAwait(false);
usersFound = usersBatchResponse.Value;
Expand Down
Loading

0 comments on commit b0e36a0

Please sign in to comment.