def oauth_params(self, action, method='POST', uid=None, key=None): secret = None cohort = None oauth_credentials = getattr(settings, 'LTI_OAUTH_CREDENTIALS', {}) if not key: keys = oauth_credentials.keys() if len(keys): key = keys[0] else: cohort = Cohort.objects.first() if cohort: key = cohort.oauth_key if key: if key in oauth_credentials: secret = oauth_credentials.get(key) else: if not cohort: cohorts = Cohort.objects.filter(oauth_key=key).all() cohort = None if cohorts: cohort = cohorts[0] if cohort: secret = cohort.oauth_secret if not secret: return None consumer = Consumer(key, secret) nonce = '' for i in range(32): nonce = nonce + str(randint(0,9)) nonce = int(nonce) if not uid: uid = str(nonce) signer = SignatureMethod() oauth_params = { 'oauth_consumer_key': key, 'oauth_signature_method': signer.name, 'oauth_timestamp': str(int(time.time())), 'oauth_nonce': str(nonce), 'user_id': uid, 'oauth_version': '1.0', 'lti_message_type': 'basic-lti-launch-request', } request = Request(method, action, oauth_params) oauth_params['oauth_signature'] = signer.sign(request, consumer, None) return oauth_params
def testRequestAvatarId(self): """ L{FacadeOAuthChecker.requestAvatarId} creates a L{FluidinfoSession} for the authenticated user only if credentials are correct. """ UserAPI().create([(u'consumer', u'secret', u'Consumer', u'*****@*****.**'), (u'user', u'secret', u'User', u'*****@*****.**')]) consumerUser = getUser(u'consumer') user = getUser(u'user') api = OAuthConsumerAPI() consumer = api.register(consumerUser) token = api.getAccessToken(consumerUser, user) self.store.commit() timestamp = 1314976811 headers = {'header1': 'foo'} arguments = 'argument1=bar' # FIXME This isn't ideal. It'd be better to use a hard-coded # signature, because then we'd know when something changed. It's hard # to do that, though, because the encrypted token generated by # fluiddb.util.minitoken is always different. -jkakar request = Request.from_request('GET', u'https://fluidinfo.com/foo', headers, {'argument1': 'bar'}) signature = SignatureMethod_HMAC_SHA1().sign(request, consumer, None) nonce = 'nonce' credentials = OAuthCredentials('fluidinfo.com', consumerUser.username, token.encrypt(), 'HMAC-SHA1', signature, timestamp, nonce, 'GET', u'https://fluidinfo.com/foo', headers, arguments) session = yield self.checker.requestAvatarId(credentials) self.assertEqual(user.username, session.auth.username) self.assertEqual(user.objectID, session.auth.objectID)
def testAuthenticateUserWithOAuthWithMixedCaseInToken(self): """ L{FacadeAuthMixin.authenticateUserWithOAuth} ignores the case in the username in the token. """ UserAPI().create([ (u'consumer', u'secret', u'Consumer', u'*****@*****.**'), (u'user', u'secret', u'User', u'*****@*****.**')]) consumerUser = getUser(u'consumer') user = getUser(u'user') api = OAuthConsumerAPI() consumer = api.register(consumerUser) token = dataToToken(consumer.secret, {'username': u'UseR', 'creationTime': '20121228-161823'}) self.store.commit() timestamp = 1314976811 headers = {'header1': 'foo'} arguments = 'argument1=bar' request = Request.from_request('GET', u'https://fluidinfo.com/foo', headers, {'argument1': 'bar'}) signature = SignatureMethod_HMAC_SHA1().sign(request, consumer, None) nonce = 'nonce' credentials = OAuthCredentials( 'fluidinfo.com', consumerUser.username, token, 'HMAC-SHA1', signature, timestamp, nonce, 'GET', u'https://fluidinfo.com/foo', headers, arguments) session = yield self.facade.authenticateUserWithOAuth(credentials) self.assertEqual(user.username, session.auth.username) self.assertEqual(user.objectID, session.auth.objectID)
def testAuthenticateUserWithOAuth(self): """ L{FacadeAuthMixin.authenticateUserWithOAuth} creates a L{FluidinfoSession} for the authenticated user only if credentials are correct. """ UserAPI().create([ (u'consumer', u'secret', u'Consumer', u'*****@*****.**'), (u'user', u'secret', u'User', u'*****@*****.**')]) consumerUser = getUser(u'consumer') user = getUser(u'user') api = OAuthConsumerAPI() consumer = api.register(consumerUser) token = api.getAccessToken(consumerUser, user) self.store.commit() timestamp = 1314976811 headers = {'header1': 'foo'} arguments = 'argument1=bar' request = Request.from_request('GET', u'https://fluidinfo.com/foo', headers, {'argument1': 'bar'}) signature = SignatureMethod_HMAC_SHA1().sign(request, consumer, None) nonce = 'nonce' credentials = OAuthCredentials( 'fluidinfo.com', consumerUser.username, token.encrypt(), 'HMAC-SHA1', signature, timestamp, nonce, 'GET', u'https://fluidinfo.com/foo', headers, arguments) session = yield self.facade.authenticateUserWithOAuth(credentials) self.assertEqual(user.username, session.auth.username) self.assertEqual(user.objectID, session.auth.objectID)
def _get_signed_oauth_request(self, path, method, data=None): data = data if data is not None else self._data url = self._url_base + path method = method if method else 'GET' req = Request.from_consumer_and_token(self.consumer, {}, method, url, data) req.sign_request(SignatureMethod_HMAC_SHA1(), self.consumer, None) return req
def get_access_token(self, key, secret, verifier): token = Token(key, secret) token.set_verifier(verifier) oauth_request = Request.from_consumer_and_token( self.consumer, http_method="POST", token=token, http_url=self.request_token_url) oauth_request.sign_request(SignatureMethod_HMAC_SHA1(), self.consumer, None) return self.fetch_access_token(oauth_request)
def get_oauth_request(self, url=None, token=None, **params): request = OAuthRequest.from_consumer_and_token(self.get_consumer(), token=token, http_url=url or self.ENDPOINT, parameters=params) request.sign_request(SignatureMethod_HMAC_SHA1(), self.get_consumer(), token) return request
def oauth_post_request(self, token, url, params): """Generate OAuth request, setups callback url""" if 'oauth_verifier' in self.data: params['oauth_verifier'] = self.data['oauth_verifier'] request = OAuthRequest.from_consumer_and_token(self.consumer, token=token, http_url=url, parameters=params, http_method='POST') request.sign_request(SignatureMethod_HMAC_SHA1(), self.consumer, token) return request
def get_auth_url(self, callback_url): # get request token oauth_request = Request.from_consumer_and_token( self.consumer, http_method="POST", http_url=self.request_token_url, parameters={'oauth_callback': callback_url}) oauth_request.sign_request(SignatureMethod_HMAC_SHA1(), self.consumer, None) token = self.fetch_request_token(oauth_request) # get authorization url oauth_request = Request.from_token_and_callback( token=token, http_url=self.authorization_url) return oauth_request.to_url(), token
def _get_auth_headers(self, method, url): oauth_params = { 'oauth_version': "1.0", 'oauth_nonce': generate_nonce(), 'oauth_timestamp': int(time.time()) } oauth_request = OAuthRequest(method, url, parameters=oauth_params) consumer = Consumer(self.client, self.oauth_secret) oauth_request.sign_request(SignatureMethod_HMAC_SHA1(), consumer, None) auth_headers = oauth_request.to_header() auth_headers['Authorization'] = auth_headers['Authorization'].encode( 'utf-8') return auth_headers
def __init__(self, customer_key=None, customer_secret=None): self.base_url = 'https://www.plurk.com' self.request_token_url = '/OAuth/request_token' self.authorization_url = '/OAuth/authorize' self.access_token_url = '/OAuth/access_token' self.customer_key = customer_key self.customer_secret = customer_secret self.sign_method = SignatureMethod_HMAC_SHA1() self.consumer = None self.token = None self.oauth_token = {} if self.customer_key and self.customer_secret: self.consumer = Consumer(self.customer_key, self.customer_secret)
def oauth_request(self, token, url, extra_params=None): """Generate OAuth request, setups callback url""" params = {'oauth_callback': self.redirect_uri} if extra_params: params.update(extra_params) if 'oauth_verifier' in self.data: params['oauth_verifier'] = self.data['oauth_verifier'] request = OAuthRequest.from_consumer_and_token(self.consumer, token=token, http_url=url, parameters=params) request.sign_request(SignatureMethod_HMAC_SHA1(), self.consumer, token) return request
def verifySignature(self, secret): """See L{IOAuthCredentials#verifySignature}.""" consumer = Consumer(key=self.consumerKey, secret=secret) oauthRequest = Request.from_request(self.method, self.url, headers=self.headers, query_string=self.arguments) # verify the request has been oauth authorized, we only support # HMAC-SHA1, reject OAuth signatures if they use a different method if self.signatureMethod != 'HMAC-SHA1': raise NotImplementedError('Unknown signature method: %s' % self.signatureMethod) signatureMethod = SignatureMethod_HMAC_SHA1() result = signatureMethod.check(oauthRequest, consumer, None, self.signature) return result
def build_consumer_oauth_request(backend, token, url, redirect_uri='/', oauth_verifier=None, extra_params=None): """Builds a Consumer OAuth request.""" params = {'oauth_callback': redirect_uri} if extra_params: params.update(extra_params) if oauth_verifier: params['oauth_verifier'] = oauth_verifier consumer = OAuthConsumer(*backend.get_key_and_secret()) request = OAuthRequest.from_consumer_and_token(consumer, token=token, http_url=url, parameters=params) request.sign_request(SignatureMethod_HMAC_SHA1(), consumer, token) return request
def auth(callback_url): # setup client = SimpleClient(SERVER, REQUEST_TOKEN_URL, ACCESS_TOKEN_URL) consumer = Consumer(CONSUMER_KEY, CONSUMER_SECRET) # get request token oauth_request = Request.from_consumer_and_token( consumer, http_method="POST", http_url=client.request_token_url, parameters={'oauth_callback': callback_url}) oauth_request.sign_request(SignatureMethod_HMAC_SHA1(), consumer, None) token = client.fetch_request_token(oauth_request) cache.set('app.root.view.post::oauth_token', token.key, 300) cache.set('app.root.view.post::oauth_token_secret', token.secret, 300) oauth_request = Request.from_token_and_callback(token=token, http_url=AUTHORIZATION_URL) return oauth_request.to_url()
class OAuthTest(OAuthTestCase): '''Tests a basic OAuth-based API.''' urls = 'project.api.urls' signature_method = SignatureMethod_HMAC_SHA1() @classmethod def setUpClass(cls): super(OAuthTest, cls).setUpClass() cls.two_legged_api_path = '/two/' cls.two_legged_api_url = cls.get_url(cls.two_legged_api_path) cls.three_legged_api_path = '/three/' cls.three_legged_api_url = cls.get_url(cls.three_legged_api_path) def setUp(self): self.session = orm.sessionmaker() self.cuser = User.create_user('testoauth', '*****@*****.**', 'testoauth') data = dict(name=u'Piston Test OAuth', description=u'A test consumer for OAuth.', session=self.session, user=self.cuser) self.consumer = Consumer.create(**data) self.old_store = getattr(settings, 'PISTON_OAUTH_STORE', None) settings.PISTON_OAUTH_STORE = BAPH_PISTON_OAUTH_STORE def tearDown(self): self.session.delete(self.cuser) self.session.delete(self.consumer) self.session.commit() del self.cuser del self.consumer settings.PISTON_OAUTH_STORE = self.old_store del self.old_store self.session.close() orm.sessionmaker_remove() def test_unauthorized(self): response = self.client.get(self.three_legged_api_path) self.assertEqual(response.status_code, 401) response = self.client.get(self.two_legged_api_path) self.assertEqual(response.status_code, 401) def test_get_request_token(self): self.get_request_token() def authorize_request_token(self, request_token_key): return super(OAuthTest, self).authorize_request_token(request_token_key, 'testoauth', 'testoauth') def test_authorize_request_token_without_callback(self): request_token = self.get_request_token('oob') response = self.authorize_request_token(request_token.key) self.assertEquals(response.status_code, 200) def test_authorize_request_token_with_callback(self): request_token = self.get_request_token(CALLBACK_URL) response = self.authorize_request_token(request_token.key) self.assertEquals(response.status_code, 302) self.assert_(response['Location'].startswith(CALLBACK_URL)) def test_get_access_token(self): self.get_access_token(CALLBACK_URL) def test_two_legged_api(self): request = Request.from_consumer_and_token(self.consumer, None, 'GET', self.two_legged_api_url, {'msg': 'expected response'}) request.sign_request(self.signature_method, self.consumer, None) response = self.client.get(self.two_legged_api_path, request) self.assertEquals(response.status_code, 200) self.assertIn('world', response.content) def test_three_legged_api(self): consumer = OAConsumer(self.consumer.key, self.consumer.secret) access_token = self.get_access_token(CALLBACK_URL) request = Request.from_consumer_and_token(consumer, access_token, 'GET', self.three_legged_api_url, {'msg': 'expected response'}) request.sign_request(self.signature_method, consumer, access_token) response = self.client.get(self.three_legged_api_path, request) self.assertEquals(response.status_code, 200) self.assertIn('world', response.content)
def __init__(self, *args, **kwargs): super(OAuthBackend, self).__init__(*args, **kwargs) self.consumer = Consumer(self.CONSUMER_KEY, self.CONSUMER_SECRET) self.signature_method = SignatureMethod_HMAC_SHA1()
class Base(object): """ Base class for all Photobucket APIs. """ # Photobucket API main endpoint DOMAIN = 'api.photobucket.com' # Used per API to define the main URI for that specific API. # E.g URI = /album/! - Will send a request to self.DOMAIN + /album/! # The ! is a special character used by Photobucket to indicate an identifier. # Ex. /album/!?id=identifier # All the APIs use an identifier for about 98% of all their methods, that's why # its already in the URI to save me the burden of putting it in almost every call :). URI = '/' # Url for user authentication. LOGIN = '******' def __init__(self, key, secret, token=None, token_secret=None, subdomain=None): """ Base API Class. All Photobucket APIs need to subclass. @key: Your photobucket API Key. @secret: Your photobucket API secret. @token: Can be a request or access token. @token_secret: Can be a request or access secret. @subdomain: The subdomain or "silo" to use when required by API. See http://bit.ly/Nla3WD """ self.key = key self.secret = secret self.token = token self.token_secret = token_secret self.subdomain = subdomain or self.DOMAIN def get_timestamp(self): return self.make_request('time', base_uri=Base.URI) # These four methods send pass Base.URI as base_uri to allow access # from any Photobucket API ( Album.ping ) since they are essential. def ping(self, method="GET"): return self.make_request('ping', base_uri=Base.URI, method=method) def login_request(self): """ Get a login request token to use during web authentication. """ return self.make_request('login/request', base_uri=Base.URI, method='POST') def get_access_token(self): return self.make_request('login/access', base_uri=Base.URI, method='POST') def get_login_url(self, token=None, extra=None): """ Returns the login url for the provided token. This assumes that token or self.token is a Request Token. """ if self.token is None and token is None: raise PhotobucketAPIError( "token needs to be set on instance or provided.") params = {} if extra: params['extra'] = extra params.update(dict(oauth_token=token or self.token)) return "%s?%s" % (self.LOGIN, urllib.urlencode(params)) def make_request(self, url, base_uri=None, params=None, auth=REQUIRED, method="GET", silo=False, **kwargs): """ Makes a request to Photobucket API. @url: The REST path to be requested after the [identifier]. By default this value is appended to self.URI. E.g. self.URI = /album/! url = /share/all The uri to request will be /album/!/share/all @base_uri: Allows for a quick override of self.URI per call. @params: A dictionary of parameters to send with the request. @auth: An Integer that determines whether this request needs to be authenticated. @method: The HTTP method to be used. @silo: Boolean. If True then this request will be sent to a specific silo/subdomain. """ params = params or dict() body = kwargs.get('body', '') headers = { 'User-Agent': 'python-photobucket/0.2 (Language=Python)', 'Content-type': 'application/x-www-form-urlencoded' } headers.update(kwargs.get('extra_headers', {})) # Unless explicitly provided, set the default response format to json. params.setdefault('format', 'json') if 'id' in params: params['id'] = self.clean_identifier(params['id']) # Remove all params with a value of "None" params = remove_empty(params) # Begin auth stuff... token = None consumer = OAuthConsumer(key=self.key, secret=self.secret) if auth in (REQUIRED, OPTIONAL): # Setup the oauth token try: token = Token(key=self.token, secret=self.token_secret) except ValueError, e: if auth == REQUIRED: # Only raise the exception if auth is required. raise PhotobucketAPIError( "Token and Token secret must be set.") # Give priority to base_uri since its a quick override of class.URI req_uri = "%s%s" % (base_uri or self.URI, url) if silo: # This request has to be sent to a specific "silo" or "subdomain". uri = "http://%s%s" % (self.subdomain, req_uri) # Don't allow redirects if this is to be sent to a specific silo. # For in photobucket's own words.. # "Photobucket ultimately prefers that you use the information given, rather than relying on the redirects" allow_redirects = False else: uri = "http://%s%s" % (self.DOMAIN, req_uri) allow_redirects = True req = OAuthRequest.from_consumer_and_token(consumer, token, method, uri, parameters=params, body=body) # Make sure to ALWAYS pass the main domain to the signature instead of the actual url to be requested. req.normalized_url = "http://%s%s" % (self.DOMAIN, req_uri) req.sign_request(SignatureMethod_HMAC_SHA1(), consumer, token) try: # I do this to take advantage of the already defined requests and their default values. response = getattr(requests, method.lower())(req.to_url(), headers=headers, allow_redirects=allow_redirects) #response.raise_for_status(allow_redirects=allow_redirects) except AttributeError: raise PhotobucketAPIError('Invalid Http method') except HTTPError, e: # This whole handling is still in Beta. # Because I'm still deciding on whether to keep it # or use "safe_mode" for all "POST" requests. To take advantage of Photobucket's redirect. # Suggestions are more than welcome... if e.response.status_code == REDIRECT: # Need to catch a redirect error because that means that user sent a request # without a "silo" so it needs to be stored. content = self.parse_response(e.response.content, params['format']) # Not too sure about this... self.subdomain = content['content']['subdomain'].split('//')[1] return self.make_request(url, base_uri, params, auth, method, silo, **kwargs) error = PhotobucketError(e.message) error.response = e.response raise error