/// <summary> /// Initializes a new instance of the <see cref="ContentAccessConfiguration"/> class. /// </summary> /// <param name="siteSettingName">Site Setting Name</param> /// <param name="sourceEntityName">Source Entity Name</param> /// <param name="targetEntityName">Target Entity Name</param> /// <param name="targetFromAttribute">Target From Attribute</param> /// <param name="targetToAttribute">Target To Attribute</param> /// <param name="intersectEntityName">Intersect Entity Name</param> /// <param name="intersectFromAttribute">Intersect From Attribute</param> /// <param name="intersectToAttribute">Intersect To Attribute</param> public ContentAccessConfiguration(string siteSettingName, string sourceEntityName, string targetEntityName, string targetFromAttribute, string targetToAttribute, string intersectEntityName, string intersectFromAttribute, string intersectToAttribute) { var linkEntityAliasGenerator = LinkEntityAliasGenerator.CreateInstance(); this.SiteSettingName = siteSettingName; this.SourceEntityName = sourceEntityName; this.TargetEntityName = targetEntityName; this.TargetFromAttribute = targetFromAttribute; this.TargetToAttribute = targetToAttribute; this.TargetAlias = linkEntityAliasGenerator.CreateUniqueAlias(targetEntityName); this.IntersectEntityName = intersectEntityName; this.IntersectFromAttribute = intersectFromAttribute; this.IntersectToAttribute = intersectToAttribute; this.IntersectAlias = linkEntityAliasGenerator.CreateUniqueAlias(intersectEntityName); }
/// <summary> /// Constructs a link-entity chain from the <paramref name="fetch"/> /// </summary> /// <param name="customerToProductRelationshipNamesCollection">Collection of relationships that map from Customer to Product</param> /// <param name="fetch">Fetch used to construct link chain</param> /// <param name="filter">Filter to inject conditions into</param> /// <param name="contact">Contact EntityReference</param> /// <param name="account">Account EntityReference</param> /// <param name="owningCustomerType">Owning Customer Type</param> /// <param name="linkEntityAliasGenerator">Single instance to maintain alias postfix incrementation</param> /// <returns>Root link of the constructed chain</returns> private Link BuildLinksAndFilterChain(string[] customerToProductRelationshipNamesCollection, Fetch fetch, Filter filter, EntityReference contact, EntityReference account, OwningCustomerType owningCustomerType, LinkEntityAliasGenerator linkEntityAliasGenerator) { var rootLink = new Link(); var linkDetails = this.GetLinkDetails(customerToProductRelationshipNamesCollection, owningCustomerType); foreach (var linkDetail in linkDetails) { var innermostLink = GetInnermostLink(rootLink); var currentLinkDetailConnectsToAccountOrContact = (contact != null && (linkDetail.Entity1Name == "contact" || linkDetail.Entity2Name == "contact")) || (account != null && (linkDetail.Entity1Name == "account" || linkDetail.Entity2Name == "account")); this.BuildLinksAndFilter(this.Portal.ServiceContext, linkDetail, fetch, innermostLink, filter, contact, account, currentLinkDetailConnectsToAccountOrContact, linkEntityAliasGenerator); } return(rootLink); }
/// <summary> /// Modify a fetch and add necessary link entity elements and filter conditions to satisfy record level security trimming based on the relationship definitions. /// </summary> /// <param name="serviceContext"><see cref="OrganizationServiceContext"/> to use</param> /// <param name="linkDetails"><see cref="ContentAccessProvider.LinkDetails"/> to use</param> /// <param name="fetch">Fetch to modify</param> /// <param name="link">Link to construct</param> /// <param name="filter">Filter to construct</param> /// <param name="contact">Associated Contact</param> /// <param name="account">Associated Account</param> /// <param name="addCondition">Construct Account/Contact relationship filter</param> /// <param name="linkEntityAliasGenerator">LinkEntityAliasGenerator to track and create Aliases</param> private void BuildLinksAndFilter(OrganizationServiceContext serviceContext, LinkDetails linkDetails, Fetch fetch, Link link, Filter filter, EntityReference contact, EntityReference account, bool addCondition, LinkEntityAliasGenerator linkEntityAliasGenerator) { var relationshipMetadata = this.BuildRelationshipMetadata(serviceContext, linkDetails); ProductAccessProvider.BuildLinksAndFilter(serviceContext, relationshipMetadata, linkDetails, fetch, link, filter, contact, account, addCondition, linkEntityAliasGenerator); }
/// <summary> /// Modify a fetch and add necessary link entity elements and filter conditions to satisfy record level security trimming based on the relationship definitions. /// </summary> /// <param name="serviceContext"><see cref="OrganizationServiceContext"/> to use</param> /// <param name="relationshipMetadata">Relationship metadata that defines relationship attributes</param> /// <param name="linkDetails"><see cref="ContentAccessProvider.LinkDetails"/> to use</param> /// <param name="fetch">Fetch to modify</param> /// <param name="link">Link to construct</param> /// <param name="filter">Filter to construct</param> /// <param name="contact">Associated Contact</param> /// <param name="account">Associated Account</param> /// <param name="addCondition">Construct Account/Contact relationship filter</param> /// <param name="linkEntityAliasGenerator">LinkEntityAliasGenerator to track and create Aliases</param> private static void BuildLinksAndFilter(OrganizationServiceContext serviceContext, ProductAccessProvider.RelationshipMetadata relationshipMetadata, LinkDetails linkDetails, Fetch fetch, Link link, Filter filter, EntityReference contact, EntityReference account, bool addCondition, LinkEntityAliasGenerator linkEntityAliasGenerator) { var alias = linkEntityAliasGenerator.CreateUniqueAlias(relationshipMetadata.Entity2LogicalName); Link newLink = null; if (relationshipMetadata.RelationshipType == ProductAccessProvider.RelationshipType.ManyToManyRelationship) { var intersectLinkEntityName = relationshipMetadata.IntersectEntityName; string linkTargetFromAttribute; string linkTargetToAttribute; string linkIntersectFromAttribute; string linkIntersectToAttribute; if (relationshipMetadata.Entity1LogicalName == relationshipMetadata.Entity2LogicalName) { linkIntersectFromAttribute = relationshipMetadata.Entity2IntersectAttribute; linkIntersectToAttribute = relationshipMetadata.Entity1PrimaryIdAttribute; linkTargetFromAttribute = relationshipMetadata.Entity1PrimaryIdAttribute; linkTargetToAttribute = relationshipMetadata.Entity1IntersectAttribute; } else { linkIntersectFromAttribute = linkIntersectToAttribute = relationshipMetadata.Entity1LogicalName == linkDetails.Entity1Name ? relationshipMetadata.Entity1IntersectAttribute : relationshipMetadata.Entity2IntersectAttribute; linkTargetFromAttribute = linkTargetToAttribute = relationshipMetadata.Entity2LogicalName == linkDetails.Entity2Name ? relationshipMetadata.Entity2IntersectAttribute : relationshipMetadata.Entity1IntersectAttribute; } newLink = new Link { Name = intersectLinkEntityName, FromAttribute = linkIntersectFromAttribute, ToAttribute = linkIntersectToAttribute, Intersect = true, Visible = false, Type = JoinOperator.LeftOuter, Links = new List <Link> { new Link { Name = relationshipMetadata.Entity2LogicalName, FromAttribute = linkTargetFromAttribute, ToAttribute = linkTargetToAttribute, Alias = alias, Type = JoinOperator.LeftOuter } } }; } else if (relationshipMetadata.RelationshipType == ProductAccessProvider.RelationshipType.ManyToOneRelationship) { var linkFromAttribute = relationshipMetadata.ReferencedEntity == relationshipMetadata.Entity2LogicalName ? relationshipMetadata.ReferencedAttribute : relationshipMetadata.ReferencingAttribute; var linkToAttribute = relationshipMetadata.ReferencedEntity == relationshipMetadata.Entity2LogicalName ? relationshipMetadata.ReferencingAttribute : relationshipMetadata.ReferencedAttribute; newLink = new Link { Name = relationshipMetadata.Entity2LogicalName, FromAttribute = linkFromAttribute, ToAttribute = linkToAttribute, Type = JoinOperator.LeftOuter, Alias = alias }; } else if (relationshipMetadata.RelationshipType == ProductAccessProvider.RelationshipType.OneToManyRelationship) { var linkFromAttribute = relationshipMetadata.ReferencedEntity == relationshipMetadata.Entity2LogicalName ? relationshipMetadata.ReferencedAttribute : relationshipMetadata.ReferencingAttribute; var linkToAttribute = relationshipMetadata.ReferencedEntity == relationshipMetadata.Entity2LogicalName ? relationshipMetadata.ReferencingAttribute : relationshipMetadata.ReferencedAttribute; newLink = new Link { Name = relationshipMetadata.Entity2LogicalName, FromAttribute = linkFromAttribute, ToAttribute = linkToAttribute, Type = JoinOperator.LeftOuter, Alias = alias }; } else { throw new ApplicationException(string.Format("Retrieve relationship request failed for relationship name {0}", linkDetails.RelationshipName)); } ContentAccessProvider.AddLink(link, newLink); if (addCondition) // Only add the condition if we are at the end of the chain { var condition = new Condition { Attribute = relationshipMetadata.Entity2PrimaryIdAttribute }; if (linkDetails.Scope.HasValue && linkDetails.Scope.Value == OwningCustomerType.Contact) { condition.EntityName = alias; condition.Operator = ConditionOperator.Equal; condition.Value = contact.Id; } else if (linkDetails.Scope.HasValue && linkDetails.Scope.Value == OwningCustomerType.Account) { condition.EntityName = alias; condition.Operator = ConditionOperator.Equal; condition.Value = account.Id; } else { condition.EntityName = alias; condition.Operator = ConditionOperator.NotNull; } filter.Conditions.Add(condition); } fetch.Distinct = true; }
/// <summary> /// Retrieves the list of Products available to the current user /// </summary> /// <returns>Product entity collection</returns> public List <Guid> GetProducts() { // If anonymous user, return nothing if (this.CurrentUserEntityReference == null) { return(Enumerable.Empty <Guid>().ToList()); } var productFetch = new Fetch { Distinct = true, Entity = new FetchEntity { Name = "product", Attributes = new List <FetchAttribute> { new FetchAttribute("productid") }, Filters = new List <Filter>() } }; var associatedToAccountOrContactFilter = new Filter { Type = LogicalOperator.Or, Conditions = new List <Condition>(), Filters = new List <Filter>() }; // Get alias generator instance to maintain alias names consistency // via postfix incrementation var linkEntityAliasGenerator = LinkEntityAliasGenerator.CreateInstance(); // Retrieve Contact to Product relationships and build Entity Permission links var contactToProductRelationshipNamesCollection = this.GetDelimitedSiteSettingValueCollection(ContactToProductRelationshipNames, ContactToProductFallbackRelationshipName); var contactLink = this.BuildLinksAndFilterChain( contactToProductRelationshipNamesCollection, productFetch, associatedToAccountOrContactFilter, this.CurrentUserEntityReference, null, OwningCustomerType.Contact, linkEntityAliasGenerator); productFetch.AddLink(contactLink); if (this.ParentCustomerEntityReference != null && this.ParentCustomerEntityReference.LogicalName == "contact") { // Retrieve parent Contact to Product relationships and build Entity Permission links var parentContactLink = this.BuildLinksAndFilterChain( contactToProductRelationshipNamesCollection, productFetch, associatedToAccountOrContactFilter, this.ParentCustomerEntityReference, null, OwningCustomerType.Contact, linkEntityAliasGenerator); productFetch.AddLink(parentContactLink); } else if (this.ParentCustomerEntityReference != null && this.ParentCustomerEntityReference.LogicalName == "account") { // Retrieve Account to Product relationships and build Entity Permission links var accountToProductRelationshipNamesCollection = this.GetDelimitedSiteSettingValueCollection(AccountToProductRelationshipNames, AccountToProductFallbackRelationshipName); var accountLink = this.BuildLinksAndFilterChain( accountToProductRelationshipNamesCollection, productFetch, associatedToAccountOrContactFilter, null, this.ParentCustomerEntityReference, OwningCustomerType.Account, linkEntityAliasGenerator); productFetch.AddLink(accountLink); } var accountOrContactNotNullFilter = new Filter { Type = LogicalOperator.Or, Conditions = associatedToAccountOrContactFilter.Conditions.Select( condition => new Condition { EntityName = condition.EntityName, Attribute = condition.Attribute, Operator = ConditionOperator.NotNull }).ToList() }; // This is the AND Filter that will ensure state is Active and the Product is joined to either Contact or Account productFetch.AddFilter(new Filter { Type = LogicalOperator.And, Conditions = new List <Condition> { new Condition("statecode", ConditionOperator.Equal, 0) }, Filters = new List <Filter> { accountOrContactNotNullFilter, associatedToAccountOrContactFilter, } }); var productsCollection = productFetch.Execute(this.Portal.ServiceContext as IOrganizationService); return(productsCollection.Entities.Select(x => x.Id).ToList()); }
/// <summary> /// Add the necessary filter conditions to filter related records /// </summary> /// <param name="serviceContext"><see cref="OrganizationServiceContext"/></param> /// <param name="fetch"><see cref="Fetch"/></param> /// <param name="id">Id of the record to filter related</param> protected void AddFiltersToFetch(OrganizationServiceContext serviceContext, Fetch fetch, string id) { if (string.IsNullOrWhiteSpace(Metadata.ViewRelationshipName)) { return; } var linkEntityAliasGenerator = LinkEntityAliasGenerator.CreateInstance(fetch); var metadataManyToMany = EntityMetadata.ManyToManyRelationships.FirstOrDefault(r => r.SchemaName == Metadata.ViewRelationshipName); var relationshipManyToOne = EntityMetadata.ManyToOneRelationships.FirstOrDefault(r => r.SchemaName == Metadata.ViewRelationshipName); var relationshipOneToMany = EntityMetadata.OneToManyRelationships.FirstOrDefault(r => r.SchemaName == Metadata.ViewRelationshipName); if (metadataManyToMany != null) { var linkIntersectName = metadataManyToMany.IntersectEntityName; string linkTargetFromAttribute; string linkTargetToAttribute; string linkIntersectFromAttribute; string linkIntersectToAttribute; if (metadataManyToMany.Entity1LogicalName == metadataManyToMany.Entity2LogicalName) { linkIntersectFromAttribute = metadataManyToMany.Entity2IntersectAttribute; linkIntersectToAttribute = EntityMetadata.PrimaryIdAttribute; linkTargetFromAttribute = EntityMetadata.PrimaryIdAttribute; linkTargetToAttribute = metadataManyToMany.Entity1IntersectAttribute; } else { linkTargetFromAttribute = linkTargetToAttribute = metadataManyToMany.Entity1LogicalName == Metadata.TargetEntityName ? metadataManyToMany.Entity1IntersectAttribute : metadataManyToMany.Entity2IntersectAttribute; linkIntersectFromAttribute = linkIntersectToAttribute = metadataManyToMany.Entity1LogicalName == Metadata.ViewTargetEntityType ? metadataManyToMany.Entity1IntersectAttribute : metadataManyToMany.Entity2IntersectAttribute; } var link = new Link { Name = linkIntersectName, FromAttribute = linkIntersectFromAttribute, ToAttribute = linkIntersectToAttribute, Intersect = true, Visible = false, Links = new List <Link> { new Link { Name = Metadata.TargetEntityName, FromAttribute = linkTargetFromAttribute, ToAttribute = linkTargetToAttribute, Alias = linkEntityAliasGenerator.CreateUniqueAlias(Metadata.TargetEntityName), Filters = new List <Filter> { new Filter { Type = LogicalOperator.And, Conditions = new List <Condition> { new Condition { Attribute = linkTargetFromAttribute, Operator = ConditionOperator.Equal, Value = id } } } } } } }; if (fetch.Entity.Links == null) { fetch.Entity.Links = new List <Link> { link }; } else { fetch.Entity.Links.Add(link); } } else if (relationshipManyToOne != null) { var link = new Link { Name = Metadata.TargetEntityName, FromAttribute = relationshipManyToOne.ReferencedAttribute, ToAttribute = relationshipManyToOne.ReferencingAttribute, Alias = linkEntityAliasGenerator.CreateUniqueAlias(Metadata.TargetEntityName), Filters = new List <Filter> { new Filter { Type = LogicalOperator.And, Conditions = new List <Condition> { new Condition { Attribute = relationshipManyToOne.ReferencedAttribute, Operator = ConditionOperator.Equal, Value = id } } } } }; if (fetch.Entity.Links == null) { fetch.Entity.Links = new List <Link> { link }; } else { fetch.Entity.Links.Add(link); } } else if (relationshipOneToMany != null) { var attribute = relationshipOneToMany.ReferencedEntity == Metadata.TargetEntityName ? relationshipOneToMany.ReferencedAttribute : relationshipOneToMany.ReferencingAttribute; var filter = new Filter { Type = LogicalOperator.And, Conditions = new List <Condition> { new Condition { Attribute = attribute, Operator = ConditionOperator.Equal, Value = id } } }; AddFilterToFetch(fetch, filter); } else { throw new ApplicationException(string.Format("RetrieveRelationshipRequest failed for view relationship name {0}", Metadata.ViewRelationshipName)); } }