示例#1
0
    def parse(self, resource: Resource, content: str) -> SAMLParserInfo:
        info = SAMLParserInfo(description='SAML Metadata', expiration_time='')
        t, expire_time_offset, exception = parse_saml_metadata(
            unicode_stream(content),
            base_url=resource.url,
            opts=resource.opts,
            validation_errors=info.validation_errors,
        )

        if expire_time_offset is not None:
            now = utc_now()
            now = now.replace(microsecond=0)

            expire_time = now + expire_time_offset
            resource.expire_time = expire_time
            info.expiration_time = str(expire_time)

        if t is not None:
            resource.t = t
            resource.type = "application/samlmetadata+xml"

            for e in iter_entities(t):
                info.entities.append(e.get('entityID'))

        if exception is not None:
            resource.info.exception = exception

        return info
示例#2
0
 def __init__(self, scheduler=None) -> None:
     random.seed(self)
     self.rm = Resource(url=None, opts=ResourceOpts())  # root
     if scheduler is None:
         scheduler = make_default_scheduler()
         scheduler.start()
     self.scheduler = scheduler
     self.store = make_store_instance()
     self.icon_store = make_icon_store_instance()
     self.rm.add_watcher(self.store, scheduler=self.scheduler)
     if config.load_icons:
         self.rm.add_watcher(self.icon_store, scheduler=self.scheduler)
示例#3
0
文件: api.py 项目: clarin-eric/pyFF
    def _info(r: Resource) -> Mapping[str, Any]:
        nfo = r.info.to_dict()
        nfo['Valid'] = r.is_valid()
        nfo['Parser'] = r.last_parser
        if r.last_seen is not None:
            nfo['Last Seen'] = r.last_seen
        if len(r.children) > 0:
            nfo['Children'] = _infos(r.children)

        return nfo
示例#4
0
    def parse(self, resource: Resource, content: str) -> ParserInfo:
        info = ParserInfo(description='XRD links', expiration_time='never expires')
        t = parse_xml(unicode_stream(content))

        relt = root(t)
        for xrd in t.iter("{%s}XRD" % NS['xrd']):
            for link in xrd.findall(".//{%s}Link[@rel='%s']" % (NS['xrd'], NS['md'])):
                link_href = link.get("href")
                certs = CertDict(link)
                fingerprints = list(certs.keys())
                fp = None
                if len(fingerprints) > 0:
                    fp = fingerprints[0]
                log.debug("XRD: {} verified by {}".format(link_href, fp))
                child_opts = resource.opts.copy(update={'alias': None})
                resource.add_child(link_href, child_opts)
        resource.last_seen = utc_now().replace(microsecond=0)
        resource.expire_time = None
        resource.never_expires = True
        return info
示例#5
0
    def parse(self, resource: Resource, content: str) -> ParserInfo:
        resource.children = deque()
        info = ParserInfo(description='Directory', expiration_time='never expires')
        n = 0
        for fn in find_matching_files(content, self.extensions):
            child_opts = resource.opts.copy(update={'alias': None})
            resource.add_child("file://" + fn, child_opts)
            n += 1

        if n == 0:
            raise IOError("no entities found in {}".format(content))

        resource.never_expires = True
        resource.expire_time = None
        resource.last_seen = utc_now().replace(microsecond=0)

        return info
示例#6
0
def parse_resource(resource: Resource, content: str) -> Optional[ParserInfo]:
    for parser in _parsers:
        if parser.magic(content):
            resource.last_parser = parser
            return parser.parse(resource, content)
    return None
示例#7
0
文件: tools.py 项目: clarin-eric/pyFF
def difftool():
    """
    diff two saml metadata sources
    """
    args = parse_options("samldiff", __doc__, 'hv',
                         ['help', 'loglevel=', 'version'])
    log_args = {'level': config.loglevel}
    if config.logfile is not None:
        log_args['filename'] = config.logfile
    logging.basicConfig(**log_args)

    try:
        rm = Resource()
        rm.add(r1)
        rm.add(r2)
        store = MemoryStore()
        rm.reload(store=store)
        r1 = Resource(url=args[0], opts=ResourceOpts())
        r2 = Resource(url=args[1], opts=ResourceOpts())
        status = 0

        if r1.t.get('Name') != r2.t.get('Name'):
            status += 1
            print("Name differs: {} != {}".format(r1.t.get('Name'),
                                                  r2.t.get('Name')))

        d1 = diff(r1.t, r2.t)
        if d1:
            print("Only in {}".format(r1.url))
            print("\n+".join(d1))
            status += 2

        d2 = diff(r2.t, r1.t)
        if d2:
            print("Only in {}".format(r2.url))
            print("\n+".join(d2))
            status += 4

        s1 = dict()
        s2 = dict()
        for e1 in iter_entities(r1.t):
            s1[e1.get('entityID')] = e1
        for e2 in iter_entities(r2.t):
            s2[e2.get('entityID')] = e2
        formatter = DiffFormatter()
        for eid in set(s1.keys()).intersection(s2.keys()):
            d = diff_trees(
                s1[eid],
                s2[eid],
                formatter=formatter,
                diff_options=dict(uniqueattrs=[
                    "{urn:oasis:names:tc:SAML:2.0:metadata}entityID"
                ]),
            )
            if d:
                status += 8
                print(d)
        sys.exit(status)
    except Exception as ex:
        logging.debug(traceback.format_exc())
        logging.error(ex)
        sys.exit(-1)
示例#8
0
class MDRepository:
    """A class representing a set of SAML metadata and the resources from where this metadata was loaded."""
    def __init__(self, scheduler=None) -> None:
        random.seed(self)
        self.rm = Resource(url=None, opts=ResourceOpts())  # root
        if scheduler is None:
            scheduler = make_default_scheduler()
            scheduler.start()
        self.scheduler = scheduler
        self.store = make_store_instance()
        self.icon_store = make_icon_store_instance()
        self.rm.add_watcher(self.store, scheduler=self.scheduler)
        if config.load_icons:
            self.rm.add_watcher(self.icon_store, scheduler=self.scheduler)

    def _lookup(self, member, store=None):
        if store is None:
            store = self.store

        if member is None:
            member = "entities"

        if is_text(member):
            if '!' in member:
                (src, xp) = member.split("!")
                if len(src) == 0:
                    src = None
                return self.lookup(src, xp=xp, store=store)

        log.debug("calling store lookup %s" % member)
        return store.lookup(member)

    def lookup(self, member, xp=None, store=None):
        """
        Lookup elements in the working metadata repository

        :param member: A selector (cf below)
        :type member: basestring
        :param xp: An optional xpath filter
        :type xp: basestring
        :param store: the store to operate on
        :return: An iterable of EntityDescriptor elements
        :rtype: etree.Element


        **Selector Syntax**

            - selector "+" selector
            - [sourceID] "!" xpath
            - attribute=value or {attribute}value
            - entityID
            - source (typically @Name from an EntitiesDescriptor set but could also be an alias)

        The first form results in the intersection of the results of doing a lookup on the selectors. The second form
        results in the EntityDescriptor elements from the source (defaults to all EntityDescriptors) that match the
        xpath expression. The attribute-value forms results in the EntityDescriptors that contain the specified entity
        attribute pair. If non of these forms apply, the lookup is done using either source ID (normally @Name from
        the EntitiesDescriptor) or the entityID of single EntityDescriptors. If member is a URI but isn't part of
        the metadata repository then it is fetched an treated as a list of (one per line) of selectors. If all else
        fails an empty list is returned.

        """
        if store is None:
            store = self.store

        l = self._lookup(member, store=store)
        if hasattr(l, 'tag'):
            l = [l]
        elif hasattr(l, '__iter__'):
            l = list(l)

        if xp is None:
            return l
        else:
            log.debug("filtering %d entities using xpath %s" % (len(l), xp))
            t = entitiesdescriptor(l, 'dummy', lookup_fn=self.lookup)
            if t is None:
                return []
            l = root(t).xpath(xp, namespaces=NS, smart_strings=False)
            log.debug("got %d entities after filtering" % len(l))
            return l
示例#9
0
    def test_cmp(self):
        r1 = Resource("https://mds.edugain.org", via=lambda x: x)
        r2 = Resource("https://mds.edugain.org")

        assert r1 == r2
示例#10
0
    def test_cmp(self):
        r1 = Resource("https://mds.edugain.org",
                      ResourceOpts(via=[lambda x: x]))
        r2 = Resource("https://mds.edugain.org", ResourceOpts())

        assert r1 == r2
示例#11
0
    def parse(self, resource: Resource, content: str) -> EidasMDParserInfo:
        info = EidasMDParserInfo(description='eIDAS MetadataServiceList',
                                 expiration_time='None')
        t = parse_xml(unicode_stream(content))
        if config.xinclude:
            t.xinclude()
        relt = root(t)
        info.version = relt.get('Version', '0')
        info.issue_date = relt.get('IssueDate')
        info.next_update = relt.get('NextUpdate')
        if isinstance(info.next_update, str):
            resource.expire_time = iso2datetime(info.next_update)
        elif config.respect_cache_duration:
            duration = duration2timedelta(config.default_cache_duration)
            if not duration:
                # TODO: what is the right action here?
                raise ValueError(
                    f'Invalid default cache duration: {config.default_cache_duration}'
                )
            info.next_update = utc_now().replace(microsecond=0) + duration
            resource.expire_time = info.next_update

        info.expiration_time = 'None' if not resource.expire_time else resource.expire_time.isoformat(
        )
        info.issuer_name = first_text(relt, "{%s}IssuerName" % NS['ser'])
        info.scheme_identifier = first_text(relt,
                                            "{%s}SchemeIdentifier" % NS['ser'])
        info.scheme_territory = first_text(relt,
                                           "{%s}SchemeTerritory" % NS['ser'])
        for mdl in relt.iter("{%s}MetadataList" % NS['ser']):
            for ml in mdl.iter("{%s}MetadataLocation" % NS['ser']):
                location = ml.get('Location')
                if location:
                    certs = CertDict(ml)
                    fingerprints = list(certs.keys())
                    fp = None
                    if len(fingerprints) > 0:
                        fp = fingerprints[0]

                    ep = ml.find("{%s}Endpoint" % NS['ser'])
                    if ep is not None and fp is not None:
                        args = dict(
                            country_code=mdl.get('Territory'),
                            hide_from_discovery=strtobool(
                                ep.get('HideFromDiscovery', 'false')),
                        )
                        log.debug("MDSL[{}]: {} verified by {} for country {}".
                                  format(info.scheme_territory, location, fp,
                                         args.get('country_code')))
                        child_opts = resource.opts.copy(update={'alias': None})
                        child_opts.verify = fp
                        r = resource.add_child(location, child_opts)

                        # this is specific post-processing for MDSL files
                        def _update_entities(_t, **kwargs):
                            _country_code = kwargs.get('country_code')
                            _hide_from_discovery = kwargs.get(
                                'hide_from_discovery')
                            for e in iter_entities(_t):
                                if _country_code:
                                    set_nodecountry(e, _country_code)
                                if bool(_hide_from_discovery) and is_idp(e):
                                    set_entity_attributes(
                                        e, {
                                            ATTRS['entity-category']:
                                            'http://refeds.org/category/hide-from-discovery'
                                        })
                            return _t

                        r.add_via(Lambda(_update_entities, **args))

        log.debug("Done parsing eIDAS MetadataServiceList")
        resource.last_seen = utc_now().replace(microsecond=0)
        resource.expire_time = None
        return info