Пример #1
0
    def recordsWithDirectoryBasedDelegates(self):
        """
        Fetch calendar-enabled locations and resources which have proxy
        groups assigned.
        """

        expression = CompoundExpression(
            (
                BooleanExpression(CalFieldName.hasCalendars),
                CompoundExpression(
                    (
                        ExistsExpression(CalFieldName.readOnlyProxy),
                        ExistsExpression(CalFieldName.readWriteProxy)
                    ),
                    Operand.OR
                )
            ),
            Operand.AND
        )

        records = yield self.recordsFromExpression(
            expression,
            recordTypes=(self.recordType.location, self.recordType.resource)
        )
        returnValue(records)
Пример #2
0
        def test_compoundWithMultipleExplicitRecordTypes(self):
            expression = CompoundExpression([
                CompoundExpression([
                    MatchExpression(self.service.fieldName.fullNames,
                                    u"be",
                                    matchType=MatchType.contains),
                    MatchExpression(self.service.fieldName.emailAddresses,
                                    u"be",
                                    matchType=MatchType.startsWith),
                ], Operand.OR),
                CompoundExpression([
                    MatchExpression(self.service.fieldName.fullNames,
                                    u"test",
                                    matchType=MatchType.contains),
                    MatchExpression(self.service.fieldName.emailAddresses,
                                    u"test",
                                    matchType=MatchType.startsWith),
                ], Operand.OR),
            ], Operand.AND)
            records = yield self.service.recordsFromExpression(
                expression,
                recordTypes=[
                    self.service.recordType.user, self.service.recordType.group
                ])

            # We should get back users and groups:
            self.verifyResults(records, [
                "odtestbetty", "odtestalbert", "anotherodtestalbert",
                "odtestgroupbetty", "odtestgroupalbert"
            ], ["odtestamanda", "odtestbill", "odtestgroupa", "odtestgroupb"])
Пример #3
0
    def recordsMatchingTokens(self,
                              tokens,
                              context=None,
                              limitResults=50,
                              timeoutSeconds=10):
        fields = [
            ("fullNames", MatchType.contains),
            ("emailAddresses", MatchType.startsWith),
        ]

        outer = []
        for token in tokens:
            inner = []
            for name, matchType in fields:
                inner.append(
                    MatchExpression(self.fieldName.lookupByName(name), token,
                                    matchType, MatchFlags.caseInsensitive))
            outer.append(CompoundExpression(inner, Operand.OR))

        if len(outer) == 1:
            expression = outer[0]
        else:
            expression = CompoundExpression(outer, Operand.AND)

        if context is not None:
            recordTypes = self.recordTypesForSearchContext(context)
        else:
            recordTypes = None

        results = yield self.recordsFromExpression(expression,
                                                   recordTypes=recordTypes)
        log.debug("Tokens ({t}) matched {n} records", t=tokens, n=len(results))

        returnValue(results)
Пример #4
0
    def recordsMatchingTokens(self, tokens, context=None, limitResults=None,
                              timeoutSeconds=None):
        fields = [
            ("fullNames", MatchType.contains),
            ("emailAddresses", MatchType.startsWith),
        ]

        outer = []
        for token in tokens:
            if token:
                token = token.strip()
                inner = []
                for name, matchType in fields:
                    inner.append(
                        MatchExpression(
                            self.fieldName.lookupByName(name),
                            token,
                            matchType,
                            MatchFlags.caseInsensitive
                        )
                    )
                outer.append(
                    CompoundExpression(
                        inner,
                        Operand.OR
                    )
                )

        if len(outer) == 1:
            expression = outer[0]
        else:
            expression = CompoundExpression(outer, Operand.AND)

        if context is not None:
            recordTypes = self.recordTypesForSearchContext(context)
        else:
            recordTypes = None

        # If a filter has been set, pass self.recordsFromExpression to it for
        # result processing
        if getattr(self, "_resultFilter", None):
            results = yield self._resultFilter(
                self.recordsFromExpression, tokens, expression,
                recordTypes=recordTypes, limitResults=limitResults,
                timeoutSeconds=timeoutSeconds
            )
        else:
            results = yield self.recordsFromExpression(
                expression, recordTypes=recordTypes, limitResults=limitResults,
                timeoutSeconds=timeoutSeconds
            )

        log.debug(
            "Tokens ({t}) matched {n} records",
            t=tokens, n=len(results)
        )

        returnValue(results)
Пример #5
0
        def test_compoundWithEmbeddedMultipleRecordTypes(self):
            expression = CompoundExpression(
                [
                    CompoundExpression(
                        [
                            CompoundExpression(
                                [
                                    MatchExpression(
                                        self.service.fieldName.fullNames, u"be",
                                        matchType=MatchType.contains
                                    ),
                                    MatchExpression(
                                        self.service.fieldName.emailAddresses, u"be",
                                        matchType=MatchType.startsWith
                                    ),
                                ],
                                Operand.OR
                            ),
                            CompoundExpression(
                                [
                                    MatchExpression(
                                        self.service.fieldName.fullNames, u"test",
                                        matchType=MatchType.contains
                                    ),
                                    MatchExpression(
                                        self.service.fieldName.emailAddresses, u"test",
                                        matchType=MatchType.startsWith
                                    ),
                                ],
                                Operand.OR
                            ),
                        ],
                        Operand.AND
                    ),
                    CompoundExpression(
                        [
                            MatchExpression(
                                self.service.fieldName.recordType, self.service.recordType.user,
                            ),
                            MatchExpression(
                                self.service.fieldName.recordType, self.service.recordType.group,
                            ),
                        ],
                        Operand.OR
                    ),
                ],
                Operand.AND
            )

            try:
                yield self.service.recordsFromExpression(expression)
            except QueryNotSupportedError:
                pass
            else:
                self.fail("This should have raised")
Пример #6
0
    def test_compoundExpressionAsJSON_expressions(
            self, serialize=compoundExpressionAsJSON):
        """
        L{compoundExpressionAsJSON} with 0, 1 and 2 sub-expressions.
        """
        for uids in (
            (),
            (u"UID1", ),
            (u"UID1", u"UID2"),
        ):
            subExpressions = [
                MatchExpression(FieldName.uid, uid) for uid in uids
            ]
            subExpressionsText = [
                matchExpressionAsJSON(e) for e in subExpressions
            ]

            expression = CompoundExpression(subExpressions, Operand.AND)
            json = compoundExpressionAsJSON(expression)

            expected = {
                u"type": u"CompoundExpression",
                u"expressions": subExpressionsText,
                u"operand": u"AND",
            }

            self.assertEquals(json, expected)
Пример #7
0
    def recordsMatchingFields(self,
                              fields,
                              operand=Operand.OR,
                              recordType=None):
        """
        @param fields: a iterable of tuples, each tuple consisting of:
            directory field name (C{unicode})
            search term (C{unicode})
            match flags (L{twext.who.expression.MatchFlags})
            match type (L{twext.who.expression.MatchType})
        """
        subExpressions = []
        for fieldName, searchTerm, matchFlags, matchType in fields:
            try:
                field = self.fieldName.lookupByName(fieldName)
            except ValueError:
                log.debug("Unsupported field name: {fieldName}",
                          fieldName=fieldName)
                continue
            subExpression = MatchExpression(field, searchTerm, matchType,
                                            matchFlags)
            subExpressions.append(subExpression)

        if len(subExpressions) == 1:
            expression = subExpressions[0]
        else:
            expression = CompoundExpression(subExpressions, operand)

        if recordType is not None:
            recordTypes = [recordType]
        else:
            recordTypes = None
        return self.recordsFromExpression(expression, recordTypes=recordTypes)
 def andOrExpression(propFilterAllOf, matchList):
     matchList = list(set(matchList))
     if propFilterAllOf and len(matchList) > 1:
         # add OR expression because parent will AND
         return [CompoundExpression(matchList, Operand.OR), ]
     else:
         return matchList
Пример #9
0
def compoundExpressionFromJSON(json):
    try:
        expressions_json = json["expressions"]
        operand_json = json["operand"]
    except KeyError as e:
        raise ValueError("JSON compound expression must have {!r} key.".format(
            e[0]))

    expressions = tuple(expressionFromJSON(e) for e in expressions_json)
    operand = Operand.lookupByName(operand_json)

    return CompoundExpression(expressions, operand)
Пример #10
0
def lookup(shortNames):
    service = DirectoryService()
    print(
        "Service = {service}\n"
        "Session = {service.session}\n"
        "Node = {service.node}\n"
        # "Local node = {service.localNode}\n"
        .format(service=service)
    )
    print("-" * 80)

    for shortName in shortNames:
        print("Looking up short name: {0}".format(shortName))

        record = yield service.recordWithShortName(service.recordType.user, shortName)
        if record:
            print(record.description())

        continue

        matchExpression = MatchExpression(
            service.fieldName.shortNames, shortName,
            matchType=MatchType.equals,
        )

        records = yield service.recordsFromExpression(matchExpression)
        for record in records:
            print(record.description())

        compoundExpression = CompoundExpression(
            [
                MatchExpression(
                    service.fieldName.shortNames, shortName,
                    matchType=MatchType.contains
                ),
                MatchExpression(
                    service.fieldName.emailAddresses, shortName,
                    matchType=MatchType.contains
                ),
            ],
            Operand.OR
        )

        records = yield service.recordsFromExpression(compoundExpression)
        for record in records:
            print(record.description())
Пример #11
0
    def test_compoundExpressionAsJSON_operands(
            self, serialize=compoundExpressionAsJSON):
        """
        L{compoundExpressionAsJSON} with different operands.
        """
        for operand, operandText in (
            (Operand.AND, u"AND"),
            (Operand.OR, u"OR"),
        ):
            expression = CompoundExpression((), operand)
            json = compoundExpressionAsJSON(expression)

            expected = {
                u"type": u"CompoundExpression",
                u"expressions": [],
                u"operand": operandText,
            }

            self.assertEquals(json, expected)
        def propFilterExpression(filterAllOf, propFilter):
            """
            Create an expression for a single prop-filter element.

            @param propFilter: the L{PropertyFilter} element.
            @return: (filterProperyNames, expressions) tuple.  expression==True means list all results, expression==False means no results
            """
            def matchExpression(fieldName, matchString, matchType, matchFlags):
                # special case recordType field
                if fieldName == FieldName.recordType:
                    # change kind to record type
                    matchValue = vCardKindToRecordTypeMap.get(
                        matchString.lower())
                    if matchValue is None:
                        matchValue = NamedConstant()
                        matchValue.description = u""

                    # change types and flags
                    matchFlags &= ~MatchFlags.caseInsensitive
                    matchType = MatchType.equals
                else:
                    matchValue = matchString.decode("utf-8")

                return MatchExpression(fieldName, matchValue, matchType,
                                       matchFlags)

            def definedExpression(defined, allOf):
                if constant or propFilter.filter_name in (
                        "N",
                        "FN",
                        "UID",
                        "KIND",
                ):
                    return defined  # all records have this property so no records do not have it
                else:
                    # FIXME: The startsWith expression below, which works with LDAP and OD. is not currently supported
                    return True
                    '''
                    # this may generate inefficient LDAP query string
                    matchFlags = MatchFlags_none if defined else MatchFlags.NOT
                    matchList = [matchExpression(fieldName, "", MatchType.startsWith, matchFlags) for fieldName in searchableFields]
                    return andOrExpression(allOf, matchList)
                    '''

            def andOrExpression(propFilterAllOf, matchList):
                matchList = list(set(matchList))
                if propFilterAllOf and len(matchList) > 1:
                    # add OR expression because parent will AND
                    return [
                        CompoundExpression(matchList, Operand.OR),
                    ]
                else:
                    return matchList

            def paramFilterElementExpression(
                    propFilterAllOf, paramFilterElement):  # @UnusedVariable

                params = vCardPropToParamMap.get(
                    propFilter.filter_name.upper())
                defined = params and paramFilterElement.filter_name.upper(
                ) in params

                # defined test
                if defined != paramFilterElement.defined:
                    return False

                # parameter value text match
                if defined and paramFilterElement.filters:
                    paramValues = params[
                        paramFilterElement.filter_name.upper()]
                    if paramValues and paramFilterElement.filters[
                            0].text.upper() not in paramValues:
                        return False

                return True

            def textMatchElementExpression(propFilterAllOf, textMatchElement):

                # preprocess text match strings for ds query
                def getMatchStrings(propFilter, matchString):

                    if propFilter.filter_name in (
                            "REV",
                            "BDAY",
                    ):
                        rawString = matchString
                        matchString = ""
                        for c in rawString:
                            if c not in "TZ-:":
                                matchString += c
                    elif propFilter.filter_name == "GEO":
                        matchString = ",".join(matchString.split(";"))

                    if propFilter.filter_name in (
                            "N",
                            "ADR",
                            "ORG",
                    ):
                        # for structured properties, change into multiple strings for ds query
                        if propFilter.filter_name == "ADR":
                            # split by newline and comma
                            rawStrings = ",".join(
                                matchString.split("\n")).split(",")
                        else:
                            # split by space
                            rawStrings = matchString.split(" ")

                        # remove empty strings
                        matchStrings = []
                        for oneString in rawStrings:
                            if len(oneString):
                                matchStrings += [
                                    oneString,
                                ]
                        return matchStrings

                    elif len(matchString):
                        return [
                            matchString,
                        ]
                    else:
                        return []
                    # end getMatchStrings

                if constant:
                    # FIXME: match is not implemented in twisteddaldav.query.Filter.TextMatch so use _match for now
                    return textMatchElement._match([
                        constant,
                    ])
                else:

                    matchStrings = getMatchStrings(propFilter,
                                                   textMatchElement.text)

                    if not len(matchStrings):
                        # no searching text in binary ds attributes, so change to defined/not defined case
                        if textMatchElement.negate:
                            return definedExpression(False, propFilterAllOf)
                        # else fall through to attribute exists case below
                    else:

                        # use match_type where possible depending on property/attribute mapping
                        # FIXME: case-sensitive negate will not work.  This should return all all records in that case
                        matchType = MatchType.contains
                        if propFilter.filter_name in (
                                "NICKNAME",
                                "TITLE",
                                "NOTE",
                                "UID",
                                "URL",
                                "N",
                                "ADR",
                                "ORG",
                                "REV",
                                "LABEL",
                        ):
                            if textMatchElement.match_type == "equals":
                                matchType = MatchType.equals
                            elif textMatchElement.match_type == "starts-with":
                                matchType = MatchType.startsWith
                            elif textMatchElement.match_type == "ends-with":
                                matchType = MatchType.endsWith

                        matchList = []
                        for matchString in matchStrings:
                            matchFlags = None
                            if textMatchElement.collation == "i;unicode-casemap" and textMatchElement.negate:
                                matchFlags = MatchFlags.caseInsensitive | MatchFlags.NOT
                            elif textMatchElement.collation == "i;unicode-casemap":
                                matchFlags = MatchFlags.caseInsensitive
                            elif textMatchElement.negate:
                                matchFlags = MatchFlags.NOT
                            else:
                                matchFlags = MatchFlags_none

                            matchList = [
                                matchExpression(fieldName, matchString,
                                                matchType, matchFlags)
                                for fieldName in searchableFields
                            ]
                            matchList.extend(matchList)
                        return andOrExpression(propFilterAllOf, matchList)

                # attribute exists search
                return definedExpression(True, propFilterAllOf)
                # end textMatchElementExpression()

            # searchablePropFilterAttrNames are attributes to be used by this propfilter's expression
            searchableFields = vcardPropToSearchableFieldMap.get(
                propFilter.filter_name, [])
            if isinstance(searchableFields, NamedConstant):
                searchableFields = (searchableFields, )

            constant = constantProperties.get(propFilter.filter_name)
            if not searchableFields and not constant:
                # not allAttrNames means propFilter.filter_name is not mapped
                # return None to try to match all items if this is the only property filter
                return None

            # create a textMatchElement for the IsNotDefined qualifier
            if isinstance(propFilter.qualifier, IsNotDefined):
                textMatchElement = TextMatch(
                    carddavxml.TextMatch.fromString(""))
                textMatchElement.negate = True
                propFilter.filters.append(textMatchElement)

            # if only one propFilter, then use filterAllOf as propFilterAllOf to reduce subexpressions and simplify generated query string
            if len(propFilter.filters) == 1:
                propFilterAllOf = filterAllOf
            else:
                propFilterAllOf = propFilter.propfilter_test == "allof"

            propFilterExpressions = None
            for propFilterElement in propFilter.filters:
                propFilterExpression = None
                if isinstance(propFilterElement, ParameterFilter):
                    propFilterExpression = paramFilterElementExpression(
                        propFilterAllOf, propFilterElement)
                elif isinstance(propFilterElement, TextMatch):
                    propFilterExpression = textMatchElementExpression(
                        propFilterAllOf, propFilterElement)
                propFilterExpressions = combineExpressionLists(
                    propFilterExpressions, propFilterAllOf,
                    propFilterExpression)
                if isinstance(
                        propFilterExpressions,
                        bool) and propFilterAllOf != propFilterExpression:
                    break

            if isinstance(propFilterExpressions, list):
                propFilterExpressions = list(set(propFilterExpressions))
                if propFilterExpressions and (filterAllOf != propFilterAllOf):
                    propFilterExpressions = [
                        CompoundExpression(
                            propFilterExpressions,
                            Operand.AND if propFilterAllOf else Operand.OR)
                    ]

            return propFilterExpressions