예제 #1
0
        protected override Query <InboxRecord> Search(Query <InboxRecord> query, GetArguments args)
        {
            string search = args.Search;

            if (!string.IsNullOrWhiteSpace(search))
            {
                search = search.Replace("'", "''"); // escape quotes by repeating them

                var createdByProp = nameof(InboxRecord.CreatedBy);
                var nameProp      = $"{createdByProp}.{nameof(User.Name)}";
                var name2Prop     = $"{createdByProp}.{nameof(User.Name2)}";
                var name3Prop     = $"{createdByProp}.{nameof(User.Name3)}";

                var commentProp = nameof(InboxRecord.Comment);
                var memoProp    = $"{nameof(InboxRecord.Document)}.{nameof(Document.Memo)}";

                // Prepare the filter string
                var filterString = $"{nameProp} contains '{search}' or {name2Prop} contains '{search}' or {name3Prop} contains '{search}' or {commentProp} contains '{search}' or {memoProp} contains '{search}'";

                // Apply the filter
                query = query.Filter(ExpressionFilter.Parse(filterString));
            }

            return(query);
        }
예제 #2
0
 public NewAmericanCarsQuery()
 {
     Filter     = new ExpressionFilter <Car>(x => x.Year > 2002).And(new ExpressionFilter <Car>(x => _american.Contains(x.Make)));
     Sort       = new DynamicLinqSort("Model asc");
     PageSize   = 1;
     PageNumber = 1;
 }
예제 #3
0
        /// <summary>
        /// Compliments <see cref="CheckActionPermissionsBefore(ExpressionFilter, List{TKey})"/> when the user has partial (Row level) access on a table
        /// This utility method checks (after the action has been perfromed) that the permission criteria predicate is still true for all actioned
        /// Ids, otherwise throws a <see cref="ForbiddenException"/> to roll back the transaction (the method must be called before committing the transaction
        /// </summary>
        protected async Task CheckActionPermissionsAfter(ExpressionFilter actionFilter, List <TKey> actionedIds, List <TEntity> data)
        {
            if (actionFilter != null)
            {
                // How many of those Ids is the user allowed to apply the action to
                int actionableIdsCount;
                if (data != null)
                {
                    // Optimization, if the data is already loaded by the action handler
                    // (with the action permissions filter applied), count that in memory
                    actionableIdsCount = data.Count;
                }
                else
                {
                    // If data is not loaded, a DB request is necessary to count
                    actionableIdsCount = await GetRepository()
                                         .Query <TEntity>()
                                         .Select("Id")
                                         .Filter(actionFilter)
                                         .FilterByIds(actionedIds)
                                         .CountAsync(cancellation: default);
                }

                // If permitted less than actual => Forbidden
                if (actionableIdsCount < actionedIds.Count)
                {
                    throw new ForbiddenException();
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Returns an <see cref="List{TEntity}"/> based on a custom <paramref name="filterFunc"/> applied to the query, as well as
        /// optional <paramref name="expand"/> and <paramref name="select"/> arguments, checking the user's READ permissions along the way.
        /// </summary>
        /// <param name="filterFunc">Allows any kind of filtering on the query</param>
        /// <param name="expand">Optional expand argument.</param>
        /// <param name="select">Optional select argument.</param>
        /// <param name="orderby">Optional orderby argument.</param>
        /// <param name="permissionsFilter">Optional filter argument, if null is passed the query uses the read permissions filter of the current user.</param>
        /// <param name="cancellation">The cancellation instruction.</param>
        protected async Task <List <TEntity> > GetEntitiesByCustomQuery(
            Func <EntityQuery <TEntity>, EntityQuery <TEntity> > filterFunc,
            ExpressionExpand expand,
            ExpressionSelect select,
            ExpressionOrderBy orderby,
            ExpressionFilter permissionsFilter,
            CancellationToken cancellation)
        {
            // Prepare a query of the result, and clone it
            var factory = QueryFactory();
            var query   = factory.EntityQuery <TEntity>();

            // Apply custom filter function
            query = filterFunc(query);

            // Apply read permissions
            permissionsFilter ??= await UserPermissionsFilter(PermissionActions.Read, cancellation);

            query = query.Filter(permissionsFilter);

            // Expand, Select and Order the result as specified in the Queryex agruments
            var expandedQuery = query.Expand(expand).Select(select).OrderBy(orderby ?? ExpressionOrderBy.Parse("Id")); // Required

            // Load the result into memory
            var data = await expandedQuery.ToListAsync(QueryContext(), cancellation); // this is potentially unordered, should that be a concern?

            // Return
            return(data);
        }
예제 #5
0
        public void TestExtensions_Apply_HasFilter()
        {
            var list = new List <QueryableOperatorTestClass>();

            list.Add(new QueryableOperatorTestClass
            {
                Id = 2
            });
            list.Add(new QueryableOperatorTestClass
            {
                Id = 1
            });
            list = list.OrderBy(x => x.Id).ToList();

            var filterValue   = 1;
            var defaultSorter = new ExpressionSorter <QueryableOperatorTestClass>(x => x.Id, SortDirection.Ascending);
            var filter        = new ExpressionFilter <QueryableOperatorTestClass>(x => x.Id, ComparisonType.Equal, filterValue);

            var queryOperator = new QueryableOperator <QueryableOperatorTestClass>(0, 10, defaultSorter, new List <IFilter> {
                filter
            }, null);
            var query    = list.AsQueryable().Apply(queryOperator);
            var testList = query.ToList();

            Assert.AreEqual(1, testList.Count);
            Assert.AreEqual(filterValue, testList.First().Id);
        }
예제 #6
0
 public NewAmericanCarsQuery()
 {
     Filter = new ExpressionFilter<Car>(x => x.Year > 2002).And(new ExpressionFilter<Car>(x => _american.Contains(x.Make)));
     Sort = new DynamicLinqSort("Model asc");
     PageSize = 1;
     PageNumber = 1;
 }
예제 #7
0
    public PagedResult <LogMessage> GetLogs(
        LogTimePeriod logTimePeriod,
        int pageNumber           = 1,
        int pageSize             = 100,
        Direction orderDirection = Direction.Descending,
        string?filterExpression  = null,
        string[]?logLevels       = null)
    {
        var expression = new ExpressionFilter(filterExpression);
        IReadOnlyList <LogEvent> filteredLogs = GetLogs(logTimePeriod, expression, 0, int.MaxValue);

        // This is user used the checkbox UI to toggle which log levels they wish to see
        // If an empty array or null - its implied all levels to be viewed
        if (logLevels?.Length > 0)
        {
            var logsAfterLevelFilters = new List <LogEvent>();
            var validLogType          = true;
            foreach (var level in logLevels)
            {
                // Check if level string is part of the LogEventLevel enum
                if (Enum.IsDefined(typeof(LogEventLevel), level))
                {
                    validLogType = true;
                    logsAfterLevelFilters.AddRange(filteredLogs.Where(x =>
                                                                      string.Equals(x.Level.ToString(), level, StringComparison.InvariantCultureIgnoreCase)));
                }
                else
                {
                    validLogType = false;
                }
            }

            if (validLogType)
            {
                filteredLogs = logsAfterLevelFilters;
            }
        }

        long totalRecords = filteredLogs.Count;

        // Order By, Skip, Take & Select
        IEnumerable <LogMessage> logMessages = filteredLogs
                                               .OrderBy(l => l.Timestamp, orderDirection)
                                               .Skip(pageSize * (pageNumber - 1))
                                               .Take(pageSize)
                                               .Select(x => new LogMessage
        {
            Timestamp           = x.Timestamp,
            Level               = x.Level,
            MessageTemplateText = x.MessageTemplate.Text,
            Exception           = x.Exception?.ToString(),
            Properties          = x.Properties,
            RenderedMessage     = x.RenderMessage(),
        });

        return(new PagedResult <LogMessage>(totalRecords, pageNumber, pageSize)
        {
            Items = logMessages
        });
    }
예제 #8
0
        /// <summary>
        /// Loads the expressions.
        /// </summary>
        /// <param name="iStartIndex">Start index of the i.</param>
        /// <param name="iNumItems">The i num items.</param>
        /// <param name="sFilter">The s filter.</param>
        private void LoadExpressions(int iStartIndex, int iNumItems, string sFilter)
        {
            ExpressionDto dto = ExpressionManager.GetExpressionDto(ExpressionCategory.GetExpressionCategory(ExpressionCategory.CategoryKey.Promotion).Key);

            ExpressionFilter.DataSource = dto.Expression;
            ExpressionFilter.DataBind();
        }
예제 #9
0
        protected override Task <EntityQuery <OutboxRecord> > Search(EntityQuery <OutboxRecord> query, GetArguments args, CancellationToken _)
        {
            string search = args.Search;

            if (!string.IsNullOrWhiteSpace(search))
            {
                search = search.Replace("'", "''"); // escape quotes by repeating them

                var assigneeProp = nameof(OutboxRecord.Assignee);
                var nameProp     = $"{assigneeProp}.{nameof(User.Name)}";
                var name2Prop    = $"{assigneeProp}.{nameof(User.Name2)}";
                var name3Prop    = $"{assigneeProp}.{nameof(User.Name3)}";

                var commentProp = nameof(OutboxRecord.Comment);
                var memoProp    = $"{nameof(OutboxRecord.Document)}.{nameof(Document.Memo)}";

                // Prepare the filter string
                var filterString = $"{nameProp} contains '{search}' or {name2Prop} contains '{search}' or {name3Prop} contains '{search}' or {commentProp} contains '{search}' or {memoProp} contains '{search}'";

                // Apply the filter
                query = query.Filter(ExpressionFilter.Parse(filterString));
            }

            return(Task.FromResult(query));
        }
예제 #10
0
        protected override Query <ExchangeRate> Search(Query <ExchangeRate> query, GetArguments args)
        {
            string search = args.Search;

            if (!string.IsNullOrWhiteSpace(search))
            {
                search = search.Replace("'", "''"); // escape quotes by repeating them

                var currencyProp = nameof(ExchangeRate.Currency);
                var idProp       = $"{currencyProp}.{nameof(Currency.Id)}";
                var nameProp     = $"{currencyProp}.{nameof(Currency.Name)}";
                var name2Prop    = $"{currencyProp}.{nameof(Currency.Name2)}";
                var name3Prop    = $"{currencyProp}.{nameof(Currency.Name3)}";
                var descProp     = $"{currencyProp}.{nameof(Currency.Description)}";
                var desc2Prop    = $"{currencyProp}.{nameof(Currency.Description2)}";
                var desc3Prop    = $"{currencyProp}.{nameof(Currency.Description3)}";

                // Prepare the filter string
                var filterString = $"{idProp} contains '{search}' or {nameProp} contains '{search}' or {name2Prop} contains '{search}' or {name3Prop} contains '{search}' or {descProp} contains '{search}' or {desc2Prop} contains '{search}' or {desc3Prop} contains '{search}'";

                // If the search is a date, include documents with that date
                if (DateTime.TryParse(search.Trim(), out DateTime searchDate))
                {
                    var validAsOfProp = nameof(ExchangeRate.ValidAsOf);
                    filterString = $"{filterString} or {validAsOfProp} eq {searchDate:yyyy-MM-dd}";
                }

                // Apply the filter
                query = query.Filter(ExpressionFilter.Parse(filterString));
            }

            return(query);
        }
예제 #11
0
        protected override Task <EntityQuery <LineDefinition> > Search(EntityQuery <LineDefinition> query, GetArguments args, CancellationToken _)
        {
            string search = args.Search;

            if (!string.IsNullOrWhiteSpace(search))
            {
                search = search.Replace("'", "''"); // escape quotes by repeating them

                var titleP  = nameof(LookupDefinition.TitlePlural);
                var titleP2 = nameof(LookupDefinition.TitlePlural2);
                var titleP3 = nameof(LookupDefinition.TitlePlural3);
                var titleS  = nameof(LookupDefinition.TitleSingular);
                var titleS2 = nameof(LookupDefinition.TitleSingular2);
                var titleS3 = nameof(LookupDefinition.TitleSingular3);
                var code    = nameof(LineDefinition.Code);
                var desc    = nameof(LineDefinition.Description);
                var desc2   = nameof(LineDefinition.Description2);
                var desc3   = nameof(LineDefinition.Description3);

                var filterString = $"{titleS} contains '{search}' or {titleS2} contains '{search}' or {titleS3} contains '{search}' or {titleP} contains '{search}' or {titleP2} contains '{search}' or {titleP3} contains '{search}' or {code} contains '{search}' or {desc} contains '{search}' or {desc2} contains '{search}' or {desc3} contains '{search}'";
                query = query.Filter(ExpressionFilter.Parse(filterString));
            }

            return(Task.FromResult(query));
        }
예제 #12
0
        /// <summary>
        /// Returns an <see cref="List{TEntity}"/> based on a custom filtering function applied to the query, as well as
        /// optional select and expand arguments, checking the user READ permissions along the way
        /// </summary>
        /// <param name="filterFunc">Allows any kind of filtering on the query</param>
        /// <param name="expand">Optional expand argument</param>
        /// <param name="select">Optional select argument</param>
        /// <param name="orderby">Optional select argument</param>
        /// <param name="permissionsFilter">Optional filter argument, if null is passed the q</param>
        /// <param name="cancellation">Optional select argument</param>
        protected async Task <List <TEntity> > GetEntitiesByCustomQuery(
            Func <Query <TEntity>, Query <TEntity> > filterFunc,
            ExpressionExpand expand,
            ExpressionSelect select,
            ExpressionOrderBy orderby,
            ExpressionFilter permissionsFilter,
            CancellationToken cancellation)
        {
            // Prepare a query of the result, and clone it
            var repo  = GetRepository();
            var query = repo.Query <TEntity>();

            // Apply custom filter function
            query = filterFunc(query);

            // Apply read permissions
            permissionsFilter ??= await UserPermissionsFilter(Constants.Read, cancellation);

            query = query.Filter(permissionsFilter);

            // Expand, Select and Order the result as specified in the OData agruments
            var expandedQuery = query.Expand(expand).Select(select).OrderBy(orderby ?? ExpressionOrderBy.Parse("Id")); // Required

            // Load the result into memory
            var data = await expandedQuery.ToListAsync(cancellation); // this is potentially unordered, should that be a concern?

            // TODO: This is slow and unused
            // Apply the permission masks (setting restricted fields to null) and adjust the metadata accordingly
            // await ApplyReadPermissionsMask(data, query, permissions, GetDefaultMask(), cancellation);

            // Return
            return(data);
        }
        public void TestParseFilters_MultipleFilters()
        {
            var expressionFilter1 = new ExpressionFilter <PagingQueryBindingModelTestClass>(x => x.A, ComparisonType.Equal, "S");

            var longValue         = 1L;
            var expressionFilter2 = new ExpressionFilter <PagingQueryBindingModelTestClass>(x => x.Id, ComparisonType.Equal, longValue);

            var json1 = JsonConvert.SerializeObject(expressionFilter1);
            var json2 = JsonConvert.SerializeObject(expressionFilter2);
            var model = new PagingQueryBindingModel <PagingQueryBindingModelTestClass>();

            model.Filter = new List <string> {
                json1, json2
            };

            var parsedFilters = model.ParseFilters(model.Filter);

            Assert.AreEqual(2, parsedFilters.Count);
            var firstParsedFilter = parsedFilters.First();

            Assert.AreEqual(expressionFilter1.Comparison, firstParsedFilter.Comparison);
            Assert.AreEqual(expressionFilter1.Value, firstParsedFilter.Value);
            Assert.AreEqual(expressionFilter1.Property, firstParsedFilter.Property);

            var lastParsedFilter = parsedFilters.Last();

            Assert.AreEqual(expressionFilter2.Comparison, lastParsedFilter.Comparison);
            Assert.AreEqual(expressionFilter2.Value, lastParsedFilter.Value);
            Assert.AreEqual(expressionFilter2.Property, lastParsedFilter.Property);
        }
        public void ShouldBlockInvalidTraceExpression()
        {
            var filter = new ExpressionFilter("Id < 2");

            var shouldTrace = filter.ShouldTrace(null, "Source", TraceEventType.Information, 3, "Message", null, null, null);

            Assert.IsFalse(shouldTrace);
        }
        public void ShouldAllowValidTraceExpression()
        {
            var filter = new ExpressionFilter("Id < 2");

            var shouldTrace = filter.ShouldTrace(null, "Source", TraceEventType.Information, 1, "Message", null, null, null);

            Assert.IsTrue(shouldTrace);
        }
        public void TestCreateGetParticipantPersonSevisCommStatusesByParticipantIdQuery_Filtered()
        {
            var userAccount = new UserAccount
            {
                PrincipalId  = 100,
                DisplayName  = "display name",
                EmailAddress = "email"
            };
            var participant = new Participant
            {
                ParticipantId = 1,
                ProjectId     = 100
            };
            var participantPerson = new ParticipantPerson
            {
                ParticipantId = participant.ParticipantId,
                Participant   = participant
            };

            participant.ParticipantPerson = participantPerson;
            var sevisCommStatus = new SevisCommStatus
            {
                SevisCommStatusId   = 500,
                SevisCommStatusName = "sevis comm status name"
            };
            var status = new ParticipantPersonSevisCommStatus
            {
                Id                = 1,
                AddedOn           = DateTimeOffset.UtcNow,
                BatchId           = "batchId",
                ParticipantId     = participant.ParticipantId,
                ParticipantPerson = participantPerson,
                PrincipalId       = userAccount.PrincipalId,
                SevisCommStatus   = sevisCommStatus,
                SevisCommStatusId = sevisCommStatus.SevisCommStatusId,
                SevisOrgId        = "sevis org Id",
                SevisUsername     = "******"
            };

            context.UserAccounts.Add(userAccount);
            context.Participants.Add(participant);
            context.ParticipantPersons.Add(participantPerson);
            context.SevisCommStatuses.Add(sevisCommStatus);
            context.ParticipantPersonSevisCommStatuses.Add(status);

            var defaultSorter = new ExpressionSorter <ParticipantPersonSevisCommStatusDTO>(x => x.AddedOn, SortDirection.Descending);
            var filter        = new ExpressionFilter <ParticipantPersonSevisCommStatusDTO>(x => x.BatchId, ComparisonType.Equal, status.BatchId);
            var queryOperator = new QueryableOperator <ParticipantPersonSevisCommStatusDTO>(0, 1, defaultSorter);

            queryOperator.Filters.Add(filter);

            var results = ParticipantPersonsSevisQueries.CreateGetParticipantPersonSevisCommStatusesByParticipantIdQuery(context, participant.ProjectId, participant.ParticipantId, queryOperator);

            Assert.AreEqual(1, results.Count());
            var firstResult = results.First();

            Assert.AreEqual(status.Id, firstResult.Id);
        }
예제 #17
0
        public void ParameterLessConstructor()
        {
            // arrange/act
            ExpressionFilter expressionFilter = new ExpressionFilter();

            // assert
            Assert.Null(expressionFilter.Field);
            Assert.Null(expressionFilter.Operator);
            Assert.Null(expressionFilter.Value);
        }
예제 #18
0
        public void TestConstructor_IntProperty()
        {
            var value          = 1;
            var comparisonType = ComparisonType.Equal;
            var filter         = new ExpressionFilter <ExpressionFilterTestClass>(x => x.Id, comparisonType, value);

            Assert.AreEqual(value, filter.Value);
            Assert.AreEqual("Id", filter.Property);
            Assert.AreEqual(comparisonType.Value, filter.Comparison);
        }
예제 #19
0
        private async Task EnsureScheduledBuildFailSubscriptionExists(BuildDefinition pipeline, WebApiTeam team, bool persistChanges)
        {
            const string BuildFailureNotificationTag = "#AutomaticBuildFailureNotification";
            var          subscriptions = await service.GetSubscriptionsAsync(team.Id);

            var hasSubscription = subscriptions.Any(sub => sub.Description.Contains(BuildFailureNotificationTag));

            logger.LogInformation("Team Is Subscribed TeamId = {0} PipelineId = {1} HasSubscription = {2}", team.Id, pipeline.Id, hasSubscription);

            if (!hasSubscription)
            {
                var filterModel = new ExpressionFilterModel
                {
                    Clauses = new ExpressionFilterClause[]
                    {
                        new ExpressionFilterClause {
                            Index = 1, LogicalOperator = "", FieldName = "Status", Operator = "=", Value = "Failed"
                        },
                        new ExpressionFilterClause {
                            Index = 2, LogicalOperator = "And", FieldName = "Definition name", Operator = "=", Value = $"\\{pipeline.Project.Name}\\{pipeline.Name}"
                        },
                        new ExpressionFilterClause {
                            Index = 3, LogicalOperator = "And", FieldName = "Build reason", Operator = "=", Value = "Scheduled"
                        }
                    }
                };
                var filter = new ExpressionFilter("ms.vss-build.build-completed-event", filterModel);

                var identity = new IdentityRef
                {
                    Id  = team.Id.ToString(),
                    Url = team.IdentityUrl
                };

                var newSubscription = new NotificationSubscriptionCreateParameters
                {
                    Channel = new UserSubscriptionChannel {
                        UseCustomAddress = false
                    },
                    Description = $"A build fails {BuildFailureNotificationTag}",
                    Filter      = filter,
                    Scope       = new SubscriptionScope {
                        Type = "none", Id = pipeline.Project.Id
                    },
                    Subscriber = identity,
                };

                logger.LogInformation("Creating Subscription PipelineId = {0}, TeamId = {1}", pipeline.Id, team.Id);
                if (persistChanges)
                {
                    var subscription = await service.CreateSubscriptionAsync(newSubscription);
                }
            }
        }
예제 #20
0
        /// <summary>
        /// Returns a list of dynamic rows and optionally their count as per the specifications in <paramref name="args"/>.
        /// </summary>
        public virtual async Task <FactResult> GetFact(FactArguments args, CancellationToken cancellation)
        {
            await Initialize(cancellation);

            // Parse the parameters
            var filter  = ExpressionFilter.Parse(args.Filter);
            var orderby = ExpressionOrderBy.Parse(args.OrderBy);
            var select  = ExpressionFactSelect.Parse(args.Select);

            // Prepare the query
            var query = QueryFactory().FactQuery <TEntity>();

            // Apply read permissions
            var permissionsFilter = await UserPermissionsFilter(PermissionActions.Read, cancellation);

            query = query.Filter(permissionsFilter);

            // Apply filter
            query = query.Filter(filter);

            // Apply orderby
            orderby ??= await DefaultOrderBy(cancellation);

            query = query.OrderBy(orderby);

            // Apply the paging (Protect against DOS attacks by enforcing a maximum page size)
            var top  = args.Top;
            var skip = args.Skip;

            top   = Math.Min(top, MaximumPageSize());
            query = query.Skip(skip).Top(top);

            // Apply the select
            query = query.Select(select);

            // Load the data and count in memory
            List <DynamicRow> data;
            int?count = null;

            if (args.CountEntities)
            {
                (data, count) = await query.ToListAndCountAsync(MaximumCount, QueryContext, cancellation);
            }
            else
            {
                data = await query.ToListAsync(QueryContext, cancellation);
            }

            // Return
            return(new FactResult(data, count));
        }
예제 #21
0
        /// <summary>
        /// Returns an aggregated list of dynamic rows and any tree dimension ancestors as per the specifications in <paramref name="args"/>.
        /// </summary>
        public virtual async Task <AggregateResult> GetAggregate(GetAggregateArguments args, CancellationToken cancellation)
        {
            await Initialize(cancellation);

            // Parse the parameters
            var filter  = ExpressionFilter.Parse(args.Filter);
            var having  = ExpressionHaving.Parse(args.Having);
            var select  = ExpressionAggregateSelect.Parse(args.Select);
            var orderby = ExpressionAggregateOrderBy.Parse(args.OrderBy);

            // Prepare the query
            var query = QueryFactory().AggregateQuery <TEntity>();

            // Retrieve and Apply read permissions
            var permissionsFilter = await UserPermissionsFilter(PermissionActions.Read, cancellation);

            query = query.Filter(permissionsFilter); // Important

            // Filter and Having
            query = query.Filter(filter);
            query = query.Having(having);

            // Apply the top parameter
            var top = args.Top == 0 ? int.MaxValue : args.Top; // 0 means get all

            top   = Math.Min(top, MaximumAggregateResultSize + 1);
            query = query.Top(top);

            // Apply the select, which has the general format 'Select=A+B.C,Sum(D)'
            query = query.Select(select);

            // Apply the orderby, which has the general format 'A+B.C desc,Sum(D) asc'
            query = query.OrderBy(orderby);

            // Load the data in memory
            var output = await query.ToListAsync(QueryContext, cancellation);

            var data      = output.Rows;
            var ancestors = output.Ancestors.Select(e => new DimensionAncestorsResult(e.Result, e.IdIndex, e.MinIndex));

            // Put a limit on the number of data points returned, to prevent DoS attacks
            if (data.Count > MaximumAggregateResultSize)
            {
                var msg = _localizer["Error_NumberOfDataPointsExceedsMaximum0", MaximumAggregateResultSize];
                throw new ServiceException(msg);
            }

            // Return
            return(new AggregateResult(data, ancestors));
        }
예제 #22
0
        public void FieldOperatorValueProvided()
        {
            // arrange/act
            ExpressionFilter expressionFilter = new ExpressionFilter()
            {
                Field    = "supdocname",
                Operator = "=",
                Value    = "foo bar"
            };

            // assert
            Assert.Equal("supdocname", expressionFilter.Field);
            Assert.Equal("=", expressionFilter.Operator);
            Assert.Equal("foo bar", expressionFilter.Value);
        }
예제 #23
0
        ///// <summary>
        ///// If the user is subject to field-level access control, this method hides all the fields
        ///// that the user has no access to and modifies the metadata of the Entities accordingly
        ///// </summary>
        //protected override async Task ApplyReadPermissionsMask(
        //    List<TEntity> resultEntities,
        //    Query<TEntity> query,
        //    IEnumerable<AbstractPermission> permissions,
        //    MaskTree defaultMask,
        //    CancellationToken cancellation)
        //{
        //    bool defaultMaskIsUnrestricted = defaultMask == null || defaultMask.IsUnrestricted;
        //    bool allPermissionMasksAreEmpty = permissions.All(e => string.IsNullOrWhiteSpace(e.Mask));
        //    bool anEmptyCriteriaIsPairedWithEmptyMask =
        //        permissions.Any(e => string.IsNullOrWhiteSpace(e.Mask) && string.IsNullOrWhiteSpace(e.Criteria));

        //    if ((allPermissionMasksAreEmpty || anEmptyCriteriaIsPairedWithEmptyMask) && defaultMaskIsUnrestricted)
        //    {
        //        // Optimization: if all masks are unrestricted, or an empty criteria is paired with an empty mask then we can skip this whole ordeal
        //        return;
        //    }
        //    else
        //    {
        //        // Maps every Entity to its list of masks
        //        var maskedEntities = new Dictionary<Entity, HashSet<string>>();
        //        var unrestrictedEntities = new HashSet<Entity>();

        //        // Marks the Entity and all Entities reachable from it as unrestricted
        //        void MarkUnrestricted(Entity entity, Type entityType)
        //        {
        //            if (entity == null)
        //            {
        //                return;
        //            }

        //            if (maskedEntities.ContainsKey(entity))
        //            {
        //                maskedEntities.Remove(entity);
        //            }

        //            if (!unrestrictedEntities.Contains(entity))
        //            {
        //                unrestrictedEntities.Add(entity);
        //                foreach (var key in entity.EntityMetadata.Keys)
        //                {
        //                    var prop = entityType.GetProperty(key);
        //                    if (prop.PropertyType.IsList())
        //                    {
        //                        // This is a navigation collection, iterate over the rows
        //                        var collection = prop.GetValue(entity);
        //                        if (collection != null)
        //                        {
        //                            var collectionType = prop.PropertyType.CollectionType();
        //                            foreach (var row in collection.Enumerate<Entity>())
        //                            {
        //                                MarkUnrestricted(row, collectionType);
        //                            }
        //                        }
        //                    }
        //                    else
        //                    {
        //                        // This is a normal navigation property
        //                        var propValue = prop.GetValue(entity) as Entity;
        //                        var propType = prop.PropertyType;
        //                        MarkUnrestricted(propValue, propType);
        //                    }
        //                }
        //            }
        //        }

        //        // Goes over this entity and every entity reachable from it and marks each one with the accessible fields
        //        void MarkMask(Entity entity, Type entityType, MaskTree mask)
        //        {
        //            if (entity == null)
        //            {
        //                return;
        //            }

        //            if (mask.IsUnrestricted)
        //            {
        //                MarkUnrestricted(entity, entityType);
        //            }
        //            else
        //            {
        //                if (unrestrictedEntities.Contains(entity))
        //                {
        //                    // Nothing to mask in an unrestricted Entity
        //                    return;
        //                }
        //                else
        //                {
        //                    if (!maskedEntities.ContainsKey(entity))
        //                    {
        //                        // All entities will have their basic fields accessible
        //                        var accessibleFields = new HashSet<string>();
        //                        foreach (var basicField in entityType.AlwaysAccessibleFields())
        //                        {
        //                            accessibleFields.Add(basicField.Name);
        //                        }

        //                        maskedEntities[entity] = accessibleFields;
        //                    }

        //                    {
        //                        var accessibleFields = maskedEntities[entity];
        //                        foreach (var requestedField in entity.EntityMetadata.Keys)
        //                        {
        //                            var prop = entityType.GetProperty(requestedField);
        //                            if (mask.ContainsKey(requestedField))
        //                            {
        //                                // If the field is included in the mask, make it accessible
        //                                if (!accessibleFields.Contains(requestedField))
        //                                {
        //                                    accessibleFields.Add(requestedField);
        //                                }

        //                                if (prop.PropertyType.IsList())
        //                                {
        //                                    // This is a navigation collection, iterate over the rows and apply the mask subtree
        //                                    var collection = prop.GetValue(entity);
        //                                    if (collection != null)
        //                                    {
        //                                        var collectionType = prop.PropertyType.CollectionType();
        //                                        foreach (var row in collection.Enumerate<Entity>())
        //                                        {
        //                                            MarkMask(row, collectionType, mask[requestedField]);
        //                                        }
        //                                    }
        //                                }
        //                                else
        //                                {
        //                                    var foreignKeyNameAtt = prop.GetCustomAttribute<ForeignKeyAttribute>();
        //                                    if (foreignKeyNameAtt != null)
        //                                    {
        //                                        // Make sure if the navigation property is included that its foreign key is included as well
        //                                        var foreignKeyName = foreignKeyNameAtt.Name;
        //                                        if (!string.IsNullOrWhiteSpace(foreignKeyName) && !accessibleFields.Contains(foreignKeyName))
        //                                        {
        //                                            accessibleFields.Add(foreignKeyName);
        //                                        }

        //                                        // Use recursion to update the rest of the tree
        //                                        var propValue = prop.GetValue(entity) as Entity;
        //                                        var propType = prop.PropertyType;
        //                                        MarkMask(propValue, propType, mask[requestedField]);
        //                                    }
        //                                }
        //                            }
        //                        }
        //                    }
        //                }
        //            }
        //        }

        //        if (permissions.All(e => string.IsNullOrWhiteSpace(e.Criteria)))
        //        {
        //            // Having no criteria is a very common case that can be optimized by skipping the database call
        //            var addDefault = permissions.Any(p => string.IsNullOrWhiteSpace(p.Mask));
        //            var masks = permissions.Select(e => e.Mask).Where(e => !string.IsNullOrWhiteSpace(e));
        //            var maskTrees = masks.Select(mask => MaskTree.Parse(mask)).ToList();
        //            if (addDefault)
        //            {
        //                maskTrees.Add(defaultMask);
        //            }

        //            // Calculate the union of all the mask fields
        //            var maskUnion = maskTrees.Aggregate(MaskTree.BasicFieldsMaskTree(), (t1, t2) => t1.UnionWith(t2));

        //            // Mark all the entities
        //            var entityType = typeof(TEntity);
        //            foreach (var item in resultEntities)
        //            {
        //                MarkMask(item, entityType, maskUnion);
        //            }
        //        }
        //        else
        //        {
        //            // an array of every criteria and every mask
        //            var maskAndCriteriaArray = permissions
        //                .Where(e => !string.IsNullOrWhiteSpace(e.Criteria)) // Optimization: a null criteria is satisfied by the entire list of entities
        //                .GroupBy(e => e.Criteria)
        //                .Select(g => new
        //                {
        //                    Criteria = g.Key,
        //                    Mask = g.Select(e => string.IsNullOrWhiteSpace(e.Mask) ? defaultMask : MaskTree.Parse(e.Mask))
        //                    .Aggregate((t1, t2) => t1.UnionWith(t2)) // takes the union of all the mask trees
        //                }).ToArray();

        //            // This mask applies to every single entity since the criteria is null
        //            var universalMask = permissions
        //                .Where(e => string.IsNullOrWhiteSpace(e.Criteria))
        //                .Distinct()
        //                .Select(e => string.IsNullOrWhiteSpace(e.Mask) ? defaultMask : MaskTree.Parse(e.Mask))
        //                .Aggregate(MaskTree.BasicFieldsMaskTree(), (t1, t2) => t1.UnionWith(t2)); // we use a seed here since if the collection is empty this will throw an error

        //            var criteriaWithIndexes = maskAndCriteriaArray
        //                .Select((e, index) => new IndexAndCriteria { Criteria = e.Criteria, Index = index });

        //            var criteriaMapList = await query.GetIndexToIdMap<TKey>(criteriaWithIndexes, cancellation);

        //            // Go over the Ids in the result and apply all relevant masks to said entity
        //            var entityType = typeof(TEntity);
        //            var criteriaMapDictionary = criteriaMapList
        //                .GroupBy(e => e.Id)
        //                .ToDictionary(e => e.Key, e => e.ToList());

        //            foreach (var entity in resultEntities)
        //            {
        //                var id = entity.Id;
        //                MaskTree mask;

        //                if (criteriaMapDictionary.ContainsKey(id))
        //                {
        //                    // Those are entities that satisfy one or more non-null Criteria
        //                    mask = criteriaMapDictionary[id]
        //                        .Select(e => maskAndCriteriaArray[e.Index].Mask)
        //                        .Aggregate((t1, t2) => t1.UnionWith(t2))
        //                        .UnionWith(universalMask);
        //                }
        //                else
        //                {
        //                    // Those are entities that belong to the universal mask of null criteria
        //                    mask = universalMask;
        //                }

        //                MarkMask(entity, entityType, mask);
        //            }
        //        }

        //        // This where field-level security is applied, we read all masked entities and apply the
        //        // masks on them by setting the field to null and adjusting the metadata accordingly
        //        foreach (var pair in maskedEntities)
        //        {
        //            var entity = pair.Key;
        //            var accessibleFields = pair.Value;

        //            List<Action> updates = new List<Action>(entity.EntityMetadata.Keys.Count);
        //            foreach (var requestedField in entity.EntityMetadata.Keys)
        //            {
        //                if (!accessibleFields.Contains(requestedField))
        //                {
        //                    // Mark the field as restricted (we delay the call to avoid the dreadful "collection-was-modified" Exception)
        //                    updates.Add(() => entity.EntityMetadata[requestedField] = FieldMetadata.Restricted);

        //                    // Set the field to null
        //                    var prop = entity.GetType().GetProperty(requestedField);
        //                    try
        //                    {
        //                        prop.SetValue(entity, null);
        //                    }
        //                    catch (Exception ex)
        //                    {
        //                        if (prop.PropertyType.IsValueType && Nullable.GetUnderlyingType(prop.PropertyType) == null)
        //                        {
        //                            // Programmer mistake
        //                            throw new InvalidOperationException($"Entity field {prop.Name} has a non nullable type, all Entity fields must have a nullable type");
        //                        }
        //                        else
        //                        {
        //                            throw ex;
        //                        }
        //                    }
        //                }
        //            }

        //            updates.ForEach(a => a());
        //        }
        //    }
        //}

        /// <summary>
        /// Many actions are simply a list of Ids
        /// This is a utility method for permission actions that do not have a mask, it checks that the
        /// user has enough permissions to cover the entire list of Ids affected by the action, if not it
        /// throws a <see cref="ForbiddenException"/>, unless the uncovered Ids are invisible to the user
        /// or do not exist, in that case it returns a watered down list of Ids the user can perform the
        /// action on. Handling missing and invisible Ids are up to the API implementation
        /// </summary>
        protected virtual async Task <List <TKey> > CheckActionPermissionsBefore(ExpressionFilter actionFilter, List <TKey> ids)
        {
            if (actionFilter == null)
            {
                return(ids); // No row level security
            }
            else
            {
                var actionedIds = ids.Distinct();

                var baseQuery = GetRepository()
                                .Query <TEntity>()
                                .Select("Id")
                                .FilterByIds(actionedIds);

                // First query to count how many Ids the user can action
                var actionableEntities = await baseQuery
                                         .Filter(actionFilter)
                                         .ToListAsync(cancellation: default);

                if (actionableEntities.Count == actionedIds.Count())
                {
                    return(ids); // The user has permission to view and perform the action on all the Ids
                }
                else // Else Potential problem, either the user (1) can't view one or more of the Ids (2) or can't perform the action on said Ids
                {
                    // Do a second query to verify that the missing Ids are solely due to read permission (not action permissions)
                    var readFilter = await UserPermissionsFilter(Constants.Read, cancellation : default);

                    var readableIdsCount = await baseQuery
                                           .Filter(readFilter)
                                           .CountAsync(cancellation: default);

                    if (actionableEntities.Count < readableIdsCount) // Definitely a problem
                    {
                        // Trying to perform an action on Ids you can see but cannot perform that action onto
                        throw new ForbiddenException();
                    }
                    else
                    {
                        // Trying to perform an action on Ids that are invisible to you, treat them like you would treat entirely missing Ids
                        // Return the actionable Ids while preserving their order
                        var actionableIdsHash = actionableEntities.Select(e => e.Id).ToHashSet();
                        return(ids.Where(id => actionableIdsHash.Contains(id)).ToList());
                    }
                }
            }
        }
        public void GetExpressionByFilter() {
            const string content = "testing converstation";

            Member member = Instance.LoggedInMember;
            Expression expression = CreateExpression(member, content);                        
            ExpressionFilter filter = new ExpressionFilter();
            filter.Author.Add(member);
            filter.AuthoredAt.AddTerm(FilterTerm.Operator.Equal, expression.AuthoredAt);

            IList<Expression> expressions = new List<Expression>(Instance.Get.Expressions(filter));
            Assert.AreEqual(1, expressions.Count);
            Expression newConv = expressions[0];
            Assert.AreEqual(expression.ID, newConv.ID);
            Assert.AreEqual(expression.Content, newConv.Content);
            Assert.AreEqual(member, newConv.Author);
        }
예제 #25
0
        protected override Query <IdentityServerUser> Search(Query <IdentityServerUser> query, GetArguments args)
        {
            string search = args.Search;

            if (!string.IsNullOrWhiteSpace(search))
            {
                search = search.Replace("'", "''"); // escape quotes by repeating them

                var email = nameof(IdentityServerUser.Email);

                var filterString = $"{email} contains '{search}'";
                query = query.Filter(ExpressionFilter.Parse(filterString));
            }

            return(query);
        }
예제 #26
0
        public void TestToString_HasFiltersAndSorters()
        {
            var filter1 = new ExpressionFilter <QueryableOperatorTestClass>(x => x.Id, ComparisonType.Equal, 1);
            var filter2 = new ExpressionFilter <QueryableOperatorTestClass>(x => x.Id, ComparisonType.LessThan, 2);

            var sorter1 = new ExpressionSorter <QueryableOperatorTestClass>(x => x.Id, SortDirection.Descending);
            var sorter2 = new ExpressionSorter <QueryableOperatorTestClass>(x => x.Id, SortDirection.Ascending);

            var defaultSorter = new ExpressionSorter <QueryableOperatorTestClass>(x => x.Id, SortDirection.Ascending);
            var queryOperator = new QueryableOperator <QueryableOperatorTestClass>(0, 10, defaultSorter, new List <IFilter> {
                filter1, filter2
            }, new List <ISorter> {
                sorter1, sorter2
            });

            Assert.IsNotNull(queryOperator.ToString());
        }
예제 #27
0
            public static Expression GetExpression <T>(ParameterExpression param, ExpressionFilter filter)
            {
                MemberExpression   member   = Expression.Property(param, filter.PropertyName);
                ConstantExpression constant = Expression.Constant(filter.Value);

                if (filter.TypeO == "text")
                {
                    constant = Expression.Constant(Convert.ToString(filter.Value), typeof(string));
                }
                if (filter.TypeO == "int")
                {
                    constant = Expression.Constant(Convert.ToInt32(filter.Value), typeof(int));
                }
                switch (filter.Comparison)
                {
                case Comparison.Equal:
                    return(Expression.Equal(member, constant));

                case Comparison.GreaterThan:
                    return(Expression.GreaterThan(member, constant));

                case Comparison.GreaterThanOrEqual:
                    return(Expression.GreaterThanOrEqual(member, constant));

                case Comparison.LessThan:
                    return(Expression.LessThan(member, constant));

                case Comparison.LessThanOrEqual:
                    return(Expression.LessThanOrEqual(member, constant));

                case Comparison.NotEqual:
                    return(Expression.NotEqual(member, constant));

                case Comparison.Contains:
                    return(Expression.Call(member, containsMethod, constant));

                case Comparison.StartsWith:
                    return(Expression.Call(member, startsWithMethod, constant));

                case Comparison.EndsWith:
                    return(Expression.Call(member, endsWithMethod, constant));

                default:
                    return(null);
                }
            }
예제 #28
0
        /// <summary>
        /// Returns a list of entities as per the specifications in the <see cref="GetChildrenArguments{TKey}"/>
        /// </summary>
        public virtual async Task <(List <TEntity>, Extras)> GetChildrenOf(GetChildrenArguments <TKey> args, CancellationToken cancellation)
        {
            // Parse the parameters
            var expand  = ExpressionExpand.Parse(args.Expand);
            var select  = ParseSelect(args.Select);
            var filter  = ExpressionFilter.Parse(args.Filter);
            var orderby = ExpressionOrderBy.Parse("Node");
            var ids     = args.I ?? new List <TKey>();

            // Load the data
            var data = await GetEntitiesByCustomQuery(q => q.FilterByParentIds(ids, args.Roots).Filter(filter), expand, select, orderby, null, cancellation);

            var extras = await GetExtras(data, cancellation);

            // Transform and Return
            return(data, extras);
        }
예제 #29
0
        protected override Query <Agent> Search(Query <Agent> query, GetArguments args)
        {
            string search = args.Search;

            if (!string.IsNullOrWhiteSpace(search))
            {
                search = search.Replace("'", "''"); // escape quotes by repeating them

                var name  = nameof(Agent.Name);
                var name2 = nameof(Agent.Name2);
                var name3 = nameof(Agent.Name3);

                var filterString = $"{name} contains '{search}' or {name2} contains '{search}' or {name3} contains '{search}'";
                query = query.Filter(ExpressionFilter.Parse(filterString));
            }

            return(query);
        }
예제 #30
0
        protected override Task <EntityQuery <AccountType> > Search(EntityQuery <AccountType> query, GetArguments args, CancellationToken _)
        {
            string search = args.Search;

            if (!string.IsNullOrWhiteSpace(search))
            {
                search = search.Replace("'", "''"); // escape quotes by repeating them

                var name  = nameof(AccountType.Name);
                var name2 = nameof(AccountType.Name2);
                var name3 = nameof(AccountType.Name3);

                var filterString = $"{name} contains '{search}' or {name2} contains '{search}' or {name3} contains '{search}'";
                query = query.Filter(ExpressionFilter.Parse(filterString));
            }

            return(Task.FromResult(query));
        }
예제 #31
0
        protected override Task <EntityQuery <DetailsEntry> > Search(EntityQuery <DetailsEntry> query, GetArguments args, CancellationToken _)
        {
            string search = args.Search;

            if (!string.IsNullOrWhiteSpace(search))
            {
                search = search.Replace("'", "''"); // escape quotes by repeating them

                var line  = nameof(DetailsEntry.Line);
                var memo  = nameof(LineForQuery.Memo);
                var text1 = nameof(LineForQuery.Text1);

                var filterString = $"{line}.{memo} contains '{search}' or {line}.{text1} contains '{search}' ";
                query = query.Filter(ExpressionFilter.Parse(filterString));
            }

            return(Task.FromResult(query));
        }
        protected override Task <EntityQuery <DashboardDefinition> > Search(EntityQuery <DashboardDefinition> query, GetArguments args, CancellationToken _)
        {
            string search = args.Search;

            if (!string.IsNullOrWhiteSpace(search))
            {
                search = search.Replace("'", "''"); // escape quotes by repeating them

                var title  = nameof(DashboardDefinition.Title);
                var title2 = nameof(DashboardDefinition.Title2);
                var title3 = nameof(DashboardDefinition.Title3);
                var code   = nameof(DashboardDefinition.Code);

                var filterString = $"{title} contains '{search}' or {title2} contains '{search}' or {title3} contains '{search}' or {code} contains '{search}'";
                query = query.Filter(ExpressionFilter.Parse(filterString));
            }

            return(Task.FromResult(query));
        }
        public void GetExpressionByReplyReferences() {
            Expression @base = CreateExpression(Instance.LoggedInMember, "base");
            Expression reply = Instance.Create.Expression(Instance.LoggedInMember, "a reply", null, @base);
            Expression unrelated = CreateExpression(Instance.LoggedInMember, "something else");

            ExpressionFilter filter = new ExpressionFilter();
            filter.InReplyTo.Add(@base);
            ICollection<Expression> expressions = Instance.Get.Expressions(filter);

            Assert.AreEqual(1, expressions.Count);
            CollectionAssert.Contains(expressions, reply);
            CollectionAssert.DoesNotContain(expressions, unrelated);

            filter = new ExpressionFilter();
            filter.Replies.Add(reply);
            expressions = Instance.Get.Expressions(filter);

            Assert.AreEqual(1, expressions.Count);
            CollectionAssert.Contains(expressions, @base);
            CollectionAssert.DoesNotContain(expressions, unrelated);
        }
        public void GetExpressionByMentions() {
            Member firstMember = EntityFactory.CreateMember("test1");
            Expression firstExpression = CreateExpression(Instance.LoggedInMember, "testing - #1");
            firstExpression.Mentions.Add(firstMember);
            firstExpression.Save();

            Member secondMember = EntityFactory.CreateMember("test2");
            Expression secondExpression = CreateExpression(Instance.LoggedInMember, "testing - #2");
            secondExpression.Mentions.Add(secondMember);
            secondExpression.Save();

            ExpressionFilter filter = new ExpressionFilter();
            filter.Mentions.Add(firstMember);
            ICollection<Expression> expressions = Instance.Get.Expressions(filter);

            Assert.AreEqual(1, expressions.Count);
            CollectionAssert.Contains(expressions, firstExpression);
            CollectionAssert.DoesNotContain(expressions, secondExpression);

            filter = new ExpressionFilter();
            filter.Mentions.Add(secondMember);
            expressions = Instance.Get.Expressions(filter);

            Assert.AreEqual(1, expressions.Count);
            CollectionAssert.Contains(expressions, secondExpression);
            CollectionAssert.DoesNotContain(expressions, firstExpression);

            filter = new ExpressionFilter();
            filter.Mentions.Add(firstMember);
            filter.Mentions.Add(secondMember);
            expressions = Instance.Get.Expressions(filter);

            Assert.AreEqual(2, expressions.Count);
            CollectionAssert.Contains(expressions, secondExpression);
            CollectionAssert.Contains(expressions, firstExpression);
        }
        public void GetExpressionByBaseAssets() {
            Story story = EntityFactory.CreateStory("fly to the Moon using a magnet and will power", SandboxProject);
            Expression firstExpression = CreateExpression(Instance.LoggedInMember, "testing - #1");
            firstExpression.Mentions.Add(story);
            firstExpression.Save();

            Test test = EntityFactory.CreateTest("check the direction", story);
            Expression secondExpression = CreateExpression(Instance.LoggedInMember, "testing - #2");
            secondExpression.Mentions.Add(test);
            secondExpression.Save();

            ExpressionFilter filter = new ExpressionFilter();
            filter.Mentions.Add(story);
            ICollection<Expression> expressions = Instance.Get.Expressions(filter);

            Assert.AreEqual(1, expressions.Count);
            CollectionAssert.Contains(expressions, firstExpression);
            CollectionAssert.DoesNotContain(expressions, secondExpression);

            filter = new ExpressionFilter();
            filter.Mentions.Add(test);
            expressions = Instance.Get.Expressions(filter);

            Assert.AreEqual(1, expressions.Count);
            CollectionAssert.Contains(expressions, secondExpression);
            CollectionAssert.DoesNotContain(expressions, firstExpression);

            filter = new ExpressionFilter();
            filter.Mentions.Add(story);
            filter.Mentions.Add(test);
            expressions = Instance.Get.Expressions(filter);

            Assert.AreEqual(2, expressions.Count);
            CollectionAssert.Contains(expressions, firstExpression);
            CollectionAssert.Contains(expressions, secondExpression);
        }