def __init__(self,
                 identity,
                 return_to,
                 trust_root=None,
                 immediate=False,
                 assoc_handle=None):
        """Construct me.

        These parameters are assigned directly as class attributes, see
        my L{class documentation<CheckIDRequest>} for their descriptions.

        @raises MalformedReturnURL: When the C{return_to} URL is not a URL.
        """
        self.assoc_handle = assoc_handle
        self.identity = identity
        self.return_to = return_to
        self.trust_root = trust_root or return_to
        if immediate:
            self.immediate = True
            self.mode = "checkid_immediate"
        else:
            self.immediate = False
            self.mode = "checkid_setup"

        if not TrustRoot.parse(self.return_to):
            raise MalformedReturnURL(None, self.return_to)
        if not self.trustRootValid():
            raise UntrustedReturnURL(None, self.return_to, self.trust_root)
 def runTest(self):
     tr = TrustRoot.parse(self.tr)
     match = tr.validateURL(self.rt)
     if self.match:
         assert match
     else:
         assert not match
Exemple #3
0
 def runTest(self):
     tr = TrustRoot.parse(self.case)
     if self.sanity == 'sane':
         assert tr.isSane(), self.case
     elif self.sanity == 'insane':
         assert not tr.isSane(), self.case
     else:
         assert tr is None, tr
    def trustRootValid(self):
        """Is my return_to under my trust_root?

        @returntype: bool
        """
        if not self.trust_root:
            return True
        tr = TrustRoot.parse(self.trust_root)
        if tr is None:
            raise MalformedTrustRoot(None, self.trust_root)
        return tr.validateURL(self.return_to)
    def trustRootValid(self):
        """Is my return_to under my trust_root?

        @returntype: bool
        """
        if not self.trust_root:
            return True
        tr = TrustRoot.parse(self.trust_root)
        if tr is None:
            raise MalformedTrustRoot(None, self.trust_root)
        return tr.validateURL(self.return_to)
    def test(self):
        ph, pdat, mh, mdat = parseTests(trustroot_test_data)

        for expected_match, desc, line in getTests([1, 0], mh, mdat):
            tr, rt = line.split()
            tr = TrustRoot.parse(tr)
            self.assertIsNotNone(tr)

            match = tr.validateURL(rt)
            if expected_match:
                assert match
            else:
                assert not match
Exemple #7
0
    def fromOpenIDRequest(cls, openid_request):
        """Extract a FetchRequest from an OpenID message

        @param openid_request: The OpenID authentication request
            containing the attribute fetch request
        @type openid_request: C{L{openid.server.server.CheckIDRequest}}

        @rtype: C{L{FetchRequest}} or C{None}
        @returns: The FetchRequest extracted from the message or None, if
            the message contained no AX extension.

        @raises KeyError: if the AuthRequest is not consistent in its use
            of namespace aliases.

        @raises AXError: When parseExtensionArgs would raise same.

        @see: L{parseExtensionArgs}
        """
        message = openid_request.message
        ax_args = message.getArgs(cls.ns_uri)
        self = cls()
        try:
            self.parseExtensionArgs(ax_args)
        except NotAXMessage:
            return None

        if self.update_url:
            # Update URL must match the openid.realm of the underlying
            # OpenID 2 message.
            realm = message.getArg(OPENID_NS, 'realm',
                                   message.getArg(OPENID_NS, 'return_to'))

            if not realm:
                raise AXError(
                    ("Cannot validate update_url %r " + "against absent realm")
                    % (self.update_url, ))

            tr = TrustRoot.parse(realm)
            if not tr.validateURL(self.update_url):
                raise AXError(
                    "Update URL %r failed validation against realm %r" % (
                        self.update_url,
                        realm,
                    ))

        return self
Exemple #8
0
    def fromOpenIDRequest(cls, openid_request):
        """Extract a FetchRequest from an OpenID message

        @param openid_request: The OpenID authentication request
            containing the attribute fetch request
        @type openid_request: C{L{openid.server.server.CheckIDRequest}}

        @rtype: C{L{FetchRequest}} or C{None}
        @returns: The FetchRequest extracted from the message or None, if
            the message contained no AX extension.

        @raises KeyError: if the AuthRequest is not consistent in its use
            of namespace aliases.

        @raises AXError: When parseExtensionArgs would raise same.

        @see: L{parseExtensionArgs}
        """
        message = openid_request.message
        ax_args = message.getArgs(cls.ns_uri)
        self = cls()
        try:
            self.parseExtensionArgs(ax_args)
        except NotAXMessage as err:
            return None

        if self.update_url:
            # Update URL must match the openid.realm of the underlying
            # OpenID 2 message.
            realm = message.getArg(OPENID_NS, 'realm',
                                   message.getArg(OPENID_NS, 'return_to'))

            if not realm:
                raise AXError(
                    ("Cannot validate update_url %r " + "against absent realm")
                    % (self.update_url, ))

            tr = TrustRoot.parse(realm)
            if not tr.validateURL(self.update_url):
                raise AXError(
                    "Update URL %r failed validation against realm %r" %
                    (self.update_url, realm, ))

        return self
Exemple #9
0
def validate_trust_root(openid_request):
    """
    Only allow OpenID requests from valid trust roots
    """

    trusted_roots = getattr(settings, 'OPENID_PROVIDER_TRUSTED_ROOT', None)

    if not trusted_roots:
        # not using trusted roots
        return True

    # don't allow empty trust roots
    if (not hasattr(openid_request, 'trust_root') or
            not openid_request.trust_root):
        log.error('no trust_root')
        return False

    # ensure trust root parses cleanly (one wildcard, of form *.foo.com, etc.)
    trust_root = TrustRoot.parse(openid_request.trust_root)
    if not trust_root:
        log.error('invalid trust_root')
        return False

    # don't allow empty return tos
    if (not hasattr(openid_request, 'return_to') or
            not openid_request.return_to):
        log.error('empty return_to')
        return False

    # ensure return to is within trust root
    if not trust_root.validateURL(openid_request.return_to):
        log.error('invalid return_to')
        return False

    # check that the root matches the ones we trust
    if not any(r for r in trusted_roots if fnmatch.fnmatch(trust_root, r)):
        log.error('non-trusted root')
        return False

    return True
Exemple #10
0
def validate_trust_root(openid_request):
    """
    Only allow OpenID requests from valid trust roots
    """

    trusted_roots = getattr(settings, 'OPENID_PROVIDER_TRUSTED_ROOT', None)

    if not trusted_roots:
        # not using trusted roots
        return True

    # don't allow empty trust roots
    if (not hasattr(openid_request, 'trust_root') or
            not openid_request.trust_root):
        log.error('no trust_root')
        return False

    # ensure trust root parses cleanly (one wildcard, of form *.foo.com, etc.)
    trust_root = TrustRoot.parse(openid_request.trust_root)
    if not trust_root:
        log.error('invalid trust_root')
        return False

    # don't allow empty return tos
    if (not hasattr(openid_request, 'return_to') or
            not openid_request.return_to):
        log.error('empty return_to')
        return False

    # ensure return to is within trust root
    if not trust_root.validateURL(openid_request.return_to):
        log.error('invalid return_to')
        return False

    # check that the root matches the ones we trust
    if not any(r for r in trusted_roots if fnmatch.fnmatch(trust_root, r)):
        log.error('non-trusted root')
        return False

    return True
    def __init__(self, identity, return_to, trust_root=None, immediate=False,
                 assoc_handle=None):
        """Construct me.

        These parameters are assigned directly as class attributes, see
        my L{class documentation<CheckIDRequest>} for their descriptions.

        @raises MalformedReturnURL: When the C{return_to} URL is not a URL.
        """
        self.assoc_handle = assoc_handle
        self.identity = identity
        self.return_to = return_to
        self.trust_root = trust_root or return_to
        if immediate:
            self.immediate = True
            self.mode = "checkid_immediate"
        else:
            self.immediate = False
            self.mode = "checkid_setup"

        if not TrustRoot.parse(self.return_to):
            raise MalformedReturnURL(None, self.return_to)
        if not self.trustRootValid():
            raise UntrustedReturnURL(None, self.return_to, self.trust_root)
    def fromQuery(klass, query):
        """Construct me from a web query.

        @raises ProtocolError: When not all required parameters are present
            in the query.

        @raises MalformedReturnURL: When the C{return_to} URL is not a URL.

        @raises UntrustedReturnURL: When the C{return_to} URL is outside
            the C{trust_root}.

        @param query: The query parameters as a dictionary with each
            key mapping to one value.
        @type query: dict

        @returntype: L{CheckIDRequest}
        """
        self = klass.__new__(klass)
        mode = query[OPENID_PREFIX + 'mode']
        if mode == "checkid_immediate":
            self.immediate = True
            self.mode = "checkid_immediate"
        else:
            self.immediate = False
            self.mode = "checkid_setup"

        required = [
            'identity',
            'return_to',
            ]

        for field in required:
            value = query.get(OPENID_PREFIX + field)
            if not value:
                raise ProtocolError(
                    query,
                    text="Missing required field %s from %r"
                    % (field, query))
            setattr(self, field, value)

        # There's a case for making self.trust_root be a TrustRoot
        # here.  But if TrustRoot isn't currently part of the "public" API,
        # I'm not sure it's worth doing.
        self.trust_root = query.get(OPENID_PREFIX + 'trust_root', self.return_to)
        self.assoc_handle = query.get(OPENID_PREFIX + 'assoc_handle')

        # Using TrustRoot.parse here is a bit misleading, as we're not
        # parsing return_to as a trust root at all.  However, valid URLs
        # are valid trust roots, so we can use this to get an idea if it
        # is a valid URL.  Not all trust roots are valid return_to URLs,
        # however (particularly ones with wildcards), so this is still a
        # little sketchy.
        if not TrustRoot.parse(self.return_to):
            raise MalformedReturnURL(query, self.return_to)

        # I first thought that checking to see if the return_to is within
        # the trust_root is premature here, a logic-not-decoding thing.  But
        # it was argued that this is really part of data validation.  A
        # request with an invalid trust_root/return_to is broken regardless of
        # application, right?
        if not self.trustRootValid():
            raise UntrustedReturnURL(query, self.return_to, self.trust_root)

        return self
Exemple #13
0
def _request_has_sane_trust_root(openid_request):
    """Return True if the RP's trust root looks sane."""
    assert openid_request is not None, (
        'Could not find the OpenID request')
    trust_root = TrustRoot.parse(openid_request.trust_root)
    return trust_root.isSane()
 def test_double_port_py3(self):
     # Python 3 urllib.parse complains about invalid port
     self.assertIsNone(TrustRoot.parse('http://*.example.com:80:90/'))
 def test_double_port_py2(self):
     # Python 2 urlparse silently drops the ':90' port
     trust_root = TrustRoot.parse('http://*.example.com:80:90/')
     self.assertTrue(trust_root.isSane())
     self.assertEqual(trust_root.buildDiscoveryURL(),
                      'http://www.example.com/')
    def fromQuery(klass, query):
        """Construct me from a web query.

        @raises ProtocolError: When not all required parameters are present
            in the query.

        @raises MalformedReturnURL: When the C{return_to} URL is not a URL.

        @raises UntrustedReturnURL: When the C{return_to} URL is outside
            the C{trust_root}.

        @param query: The query parameters as a dictionary with each
            key mapping to one value.
        @type query: dict

        @returntype: L{CheckIDRequest}
        """
        self = klass.__new__(klass)
        mode = query[OPENID_PREFIX + 'mode']
        if mode == "checkid_immediate":
            self.immediate = True
            self.mode = "checkid_immediate"
        else:
            self.immediate = False
            self.mode = "checkid_setup"

        required = [
            'identity',
            'return_to',
        ]

        for field in required:
            value = query.get(OPENID_PREFIX + field)
            if not value:
                raise ProtocolError(query,
                                    text="Missing required field %s from %r" %
                                    (field, query))
            setattr(self, field, value)

        # There's a case for making self.trust_root be a TrustRoot
        # here.  But if TrustRoot isn't currently part of the "public" API,
        # I'm not sure it's worth doing.
        self.trust_root = query.get(OPENID_PREFIX + 'trust_root',
                                    self.return_to)
        self.assoc_handle = query.get(OPENID_PREFIX + 'assoc_handle')

        # Using TrustRoot.parse here is a bit misleading, as we're not
        # parsing return_to as a trust root at all.  However, valid URLs
        # are valid trust roots, so we can use this to get an idea if it
        # is a valid URL.  Not all trust roots are valid return_to URLs,
        # however (particularly ones with wildcards), so this is still a
        # little sketchy.
        if not TrustRoot.parse(self.return_to):
            raise MalformedReturnURL(query, self.return_to)

        # I first thought that checking to see if the return_to is within
        # the trust_root is premature here, a logic-not-decoding thing.  But
        # it was argued that this is really part of data validation.  A
        # request with an invalid trust_root/return_to is broken regardless of
        # application, right?
        if not self.trustRootValid():
            raise UntrustedReturnURL(query, self.return_to, self.trust_root)

        return self
Exemple #17
0
class FetchRequest(AXMessage):
    """An attribute exchange 'fetch_request' message. This message is
    sent by a relying party when it wishes to obtain attributes about
    the subject of an OpenID authentication request.

    @ivar requested_attributes: The attributes that have been
        requested thus far, indexed by the type URI.
    @type requested_attributes: {str:AttrInfo}

    @ivar update_url: A URL that will accept responses for this
        attribute exchange request, even in the absence of the user
        who made this request.
    """
    mode = 'fetch_request'

    def __init__(self, update_url=None):
        AXMessage.__init__(self)
        self.requested_attributes = {}
        self.update_url = update_url

    def add(self, attribute):
        """Add an attribute to this attribute exchange request.

        @param attribute: The attribute that is being requested
        @type attribute: C{L{AttrInfo}}

        @returns: None

        @raise KeyError: when the requested attribute is already
            present in this fetch request.
        """
        if attribute.type_uri in self.requested_attributes:
            raise KeyError('The attribute %r has already been requested' %
                           (attribute.type_uri, ))

        self.requested_attributes[attribute.type_uri] = attribute

    def getExtensionArgs(self):
        """Get the serialized form of this attribute fetch request.

        @returns: The fetch request message parameters
        @rtype: {unicode:unicode}
        """
        aliases = NamespaceMap()

        required = []
        if_available = []

        ax_args = self._newArgs()

        for type_uri, attribute in self.requested_attributes.iteritems():
            if attribute.alias is None:
                alias = aliases.add(type_uri)
            else:
                # This will raise an exception when the second
                # attribute with the same alias is added. I think it
                # would be better to complain at the time that the
                # attribute is added to this object so that the code
                # that is adding it is identified in the stack trace,
                # but it's more work to do so, and it won't be 100%
                # accurate anyway, since the attributes are
                # mutable. So for now, just live with the fact that
                # we'll learn about the error later.
                #
                # The other possible approach is to hide the error and
                # generate a new alias on the fly. I think that would
                # probably be bad.
                alias = aliases.addAlias(type_uri, attribute.alias)

            if attribute.required:
                required.append(alias)
            else:
                if_available.append(alias)

            if attribute.count != 1:
                ax_args['count.' + alias] = str(attribute.count)

            ax_args['type.' + alias] = type_uri

        if required:
            ax_args['required'] = ','.join(required)

        if if_available:
            ax_args['if_available'] = ','.join(if_available)

        return ax_args

    def getRequiredAttrs(self):
        """Get the type URIs for all attributes that have been marked
        as required.

        @returns: A list of the type URIs for attributes that have
            been marked as required.
        @rtype: [str]
        """
        required = []
        for type_uri, attribute in self.requested_attributes.iteritems():
            if attribute.required:
                required.append(type_uri)

        return required

    def fromOpenIDRequest(cls, openid_request):
        """Extract a FetchRequest from an OpenID message

        @param openid_request: The OpenID authentication request
            containing the attribute fetch request
        @type openid_request: C{L{openid.server.server.CheckIDRequest}}

        @rtype: C{L{FetchRequest}} or C{None}
        @returns: The FetchRequest extracted from the message or None, if
            the message contained no AX extension.

        @raises KeyError: if the AuthRequest is not consistent in its use
            of namespace aliases.

        @raises AXError: When parseExtensionArgs would raise same.

        @see: L{parseExtensionArgs}
        """
        message = openid_request.message
        ax_args = message.getArgs(cls.ns_uri)
        self = cls()
        try:
            self.parseExtensionArgs(ax_args)
        except NotAXMessage, err:
            return None

        if self.update_url:
            # Update URL must match the openid.realm of the underlying
            # OpenID 2 message.
            realm = message.getArg(OPENID_NS, 'realm',
                                   message.getArg(OPENID_NS, 'return_to'))

            if not realm:
                raise AXError(
                    ("Cannot validate update_url %r " + "against absent realm")
                    % (self.update_url, ))

            tr = TrustRoot.parse(realm)
            if not tr.validateURL(self.update_url):
                raise AXError(
                    "Update URL %r failed validation against realm %r" % (
                        self.update_url,
                        realm,
                    ))

        return self