Example #1
0
 def test_prepare_headers_ignore_data(self):
     self.assertEqual(
         prepare_headers(self.auth_and_data, {}),
         {u'Authorization': self.norealm_authorization_header})
     self.assertEqual(
         prepare_headers(self.auth_and_data, {}, realm=self.realm),
         {u'Authorization': self.withrealm_authorization_header})
Example #2
0
 def test_prepare_headers(self):
     self.assertEqual(
         prepare_headers(self.auth_only_params, {}),
         {u'Authorization': self.norealm_authorization_header})
     self.assertEqual(
         prepare_headers(self.auth_only_params, {}, realm=self.realm),
         {u'Authorization': self.withrealm_authorization_header})
Example #3
0
 def test_prepare_headers_ignore_data(self):
     self.assertEqual(
         prepare_headers(self.auth_and_data, {}),
         {'Authorization': self.norealm_authorization_header})
     self.assertEqual(
         prepare_headers(self.auth_and_data, {}, realm=self.realm),
         {'Authorization': self.withrealm_authorization_header})
Example #4
0
 def test_prepare_headers(self):
     self.assertEqual(
         prepare_headers(self.auth_only_params, {}),
         {'Authorization': self.norealm_authorization_header})
     self.assertEqual(
         prepare_headers(self.auth_only_params, {}, realm=self.realm),
         {'Authorization': self.withrealm_authorization_header})
Example #5
0
    def _oauth_sign(self, url, body, content_type='application/x-www-form-urlencoded', method='POST'):
        """
        Signs request and returns signed Authorization header.
        """
        client_key = self.server.config.get('client_key', self.DEFAULT_CLIENT_KEY)
        client_secret = self.server.config.get('client_secret', self.DEFAULT_CLIENT_SECRET)
        client = oauthlib.oauth1.Client(
            client_key=str(client_key),
            client_secret=str(client_secret)
        )
        headers = {
            # This is needed for body encoding:
            'Content-Type': content_type,
        }

        # Calculate and encode body hash. See http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html
        sha1 = hashlib.sha1()
        sha1.update(body.encode('utf-8'))
        oauth_body_hash = base64.b64encode(sha1.digest()).decode('utf-8')
        mock_request = mock.Mock(
            uri=str(six.moves.urllib.parse.unquote(url)),
            headers=headers,
            body="",
            decoded_body="",
            http_method=str(method),
        )
        params = client.get_oauth_params(mock_request)
        mock_request.oauth_params = params
        mock_request.oauth_params.append(('oauth_body_hash', oauth_body_hash))
        sig = client.get_oauth_signature(mock_request)
        mock_request.oauth_params.append(('oauth_signature', sig))
        new_headers = parameters.prepare_headers(mock_request.oauth_params, headers, realm=None)
        return new_headers['Authorization']
Example #6
0
    def _oauth_sign(self, url, body, content_type=u'application/x-www-form-urlencoded', method=u'POST'):
        """
        Signs request and returns signed Authorization header.
        """
        client_key = self.server.config.get('client_key', self.DEFAULT_CLIENT_KEY)
        client_secret = self.server.config.get('client_secret', self.DEFAULT_CLIENT_SECRET)
        client = oauthlib.oauth1.Client(
            client_key=unicode(client_key),
            client_secret=unicode(client_secret)
        )
        headers = {
            # This is needed for body encoding:
            'Content-Type': content_type,
        }

        # Calculate and encode body hash. See http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html
        sha1 = hashlib.sha1()
        sha1.update(body)
        oauth_body_hash = unicode(base64.b64encode(sha1.digest()))  # pylint: disable=too-many-function-args
        params = client.get_oauth_params(None)
        params.append((u'oauth_body_hash', oauth_body_hash))
        mock_request = mock.Mock(
            uri=unicode(urllib.unquote(url)),
            headers=headers,
            body=u"",
            decoded_body=u"",
            oauth_params=params,
            http_method=unicode(method),
        )
        sig = client.get_oauth_signature(mock_request)
        mock_request.oauth_params.append((u'oauth_signature', sig))
        new_headers = parameters.prepare_headers(mock_request.oauth_params, headers, realm=None)
        return new_headers['Authorization']
Example #7
0
 def create_authorization_header(self):
     return prepare_headers(self.params)
Example #8
0
def post_grade(sourcedid, outcomes_url, consumer_key, consumer_secret, grade):
    # Who is treating XML as Text? I am!
    # WHY WOULD YOU MIX MULTIPART, XML (NOT EVEN JUST XML, BUT WSDL GENERATED POX WTF), AND PARTS OF OAUTH1 SIGNING
    # IN THE SAME STANDARD AAAA!
    # TODO: extract this into a real library with real XML parsing
    # WARNING: You can use this only with data you trust! Beware, etc.
    post_xml = r"""
    <?xml version = "1.0" encoding = "UTF-8"?> 
    <imsx_POXEnvelopeRequest xmlns = "http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0"> 
      <imsx_POXHeader> 
        <imsx_POXRequestHeaderInfo> 
          <imsx_version>V1.0</imsx_version> 
          <imsx_messageIdentifier>999999123</imsx_messageIdentifier> 
        </imsx_POXRequestHeaderInfo> 
      </imsx_POXHeader> 
      <imsx_POXBody> 
        <replaceResultRequest> 
          <resultRecord> 
            <sourcedGUID> 
              <sourcedId>{sourcedid}</sourcedId> 
            </sourcedGUID> 
            <result> 
              <resultScore> 
                <language>en</language> 
                <textString>{grade}</textString> 
              </resultScore> 
            </result> 
          </resultRecord> 
        </replaceResultRequest> 
      </imsx_POXBody> 
    </imsx_POXEnvelopeRequest>
    """
    post_data = post_xml.format(grade=float(grade), sourcedid=sourcedid)

    # Yes, we do have to use sha1 :(
    body_hash_sha = sha1()
    body_hash_sha.update(post_data.encode('utf-8'))
    body_hash = base64.b64encode(body_hash_sha.digest()).decode('utf-8')
    args = {
        'oauth_body_hash': body_hash,
        'oauth_consumer_key': consumer_key,
        'oauth_timestamp': str(time.time()),
        'oauth_nonce': str(time.time())
    }

    base_string = signature.construct_base_string(
        'POST',
        signature.normalize_base_string_uri(outcomes_url),
        signature.normalize_parameters(
            signature.collect_parameters(body=args, headers={})
        )
    )

    oauth_signature = signature.sign_hmac_sha1(base_string, consumer_secret, None)
    args['oauth_signature'] = oauth_signature

    headers = parameters.prepare_headers(args, headers={
        'Content-Type': 'application/xml'
    })    

    resp = requests.post(outcomes_url, data=post_data, headers=headers)
    if resp.status_code != 200:
        raise GradePostException(resp)

    response_tree = etree.fromstring(resp.text.encode('utf-8'))

    # XML and its namespaces. UBOOF!
    status_tree = response_tree.find('.//{http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0}imsx_statusInfo')
    code_major = status_tree.find('{http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0}imsx_codeMajor').text

    if code_major != 'success':
        raise GradePostException(resp)
Example #9
0
async def post_grade(user_id, grade, sourcedid, outcomes_url):
    # TODO: extract this into a real library with real XML parsing
    # WARNING: You can use this only with data you trust! Beware, etc.
    post_xml = r"""
    <?xml version = "1.0" encoding = "UTF-8"?>
    <imsx_POXEnvelopeRequest xmlns = "http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0">
      <imsx_POXHeader>
        <imsx_POXRequestHeaderInfo>
          <imsx_version>V1.0</imsx_version>
          <imsx_messageIdentifier>999999123</imsx_messageIdentifier>
        </imsx_POXRequestHeaderInfo>
      </imsx_POXHeader>
      <imsx_POXBody>
        <replaceResultRequest>
          <resultRecord>
            <sourcedGUID>
              <sourcedId>{sourcedid}</sourcedId>
            </sourcedGUID>
            <result>
              <resultScore>
                <language>en</language>
                <textString>{grade}</textString>
              </resultScore>
            </result>
          </resultRecord>
        </replaceResultRequest>
      </imsx_POXBody>
    </imsx_POXEnvelopeRequest>
    """
    # Assumes these are read in from a config file in Jupyterhub
    consumer_key = os.environ['LTI_CONSUMER_KEY']
    consumer_secret = os.environ['LTI_CONSUMER_SECRET']
    sourcedid = "{}:{}".format(sourcedid, user_id)
    post_data = post_xml.format(grade=float(grade), sourcedid=sourcedid)

    # Yes, we do have to use sha1 :(
    body_hash_sha = sha1()
    body_hash_sha.update(post_data.encode('utf-8'))
    body_hash = base64.b64encode(body_hash_sha.digest()).decode('utf-8')
    args = {
        'oauth_body_hash': body_hash,
        'oauth_consumer_key': consumer_key,
        'oauth_timestamp': str(time.time()),
        'oauth_nonce': str(time.time())
    }

    base_string = signature.construct_base_string(
        'POST', signature.normalize_base_string_uri(outcomes_url),
        signature.normalize_parameters(
            signature.collect_parameters(body=args, headers={})))

    oauth_signature = signature.sign_hmac_sha1(base_string, consumer_secret,
                                               None)
    args['oauth_signature'] = oauth_signature

    headers = parameters.prepare_headers(
        args, headers={'Content-Type': 'application/xml'})

    async with async_timeout.timeout(10):
        async with aiohttp.ClientSession() as session:
            async with session.post(outcomes_url,
                                    data=post_data,
                                    headers=headers) as response:
                resp_text = await response.text()

                if response.status != 200:
                    raise GradePostException(response)

    response_tree = etree.fromstring(resp_text.encode('utf-8'))

    # XML and its namespaces. UBOOF!
    status_tree = response_tree.find(
        './/{http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0}imsx_statusInfo'
    )
    code_major = status_tree.find(
        '{http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0}imsx_codeMajor'
    ).text

    if code_major != 'success':
        raise GradePostException(response)