def start_auth(self, request): try: if not self.get_arguments(request, "service"): self.write(request, error_format("Missing argument 'service'"), 400) self.finish(request) else: service_name = self.get_argument(request, "service") got_error = False try: service = self.get_service(self.get_argument(request, "service"), request) except KeyError: got_error = True self.write(request, error_format("Service '%s' unknown" % service_name), 404) if not got_error: args = self.arguments(request) del args["service"] got_error = False try: auth_url = yield service.start_authorization( client_name=self.get_client_name(request), args=args ) except LightningError as exc: got_error = True self.write(request, exc.error_msg, exc.code) if not got_error: self.write(request, {"redirect": auth_url}) self.finish(request) except Exception as exc: self.write_error(request, exc) self.finish(request)
def test_post_too_many_guids(self): return self.post_and_verify( path='/data/loopback/time', args=dict(guid=['asdf', 'efgh']), response_code=400, result=error_format("Too many GUIDs provided"), )
def test_post_unknown_guid(self): return self.post_and_verify( path='/data/loopback/time', args=dict(guid='asdf'), response_code=400, result=error_format("Unknown GUID provided"), )
def revoke_auth(self, request): service_name = self.name """Revoke OAuth token callback for service. If a user revoke's our application's permissions in the service's settings, the service will hit this callback so we can remove the user's token and any other data. """ try: signed_request = self.get_arguments(request, "signed_request") client_name = self.get_client_name(request) if not signed_request: raise HandlerError("Missing argument 'signed_request'", 400) service = self.get_service(service_name, request) user_id = yield service.service_revoke_authorization(client_name=client_name, args=self.arguments(request)) ret = yield self.application.db.delete_oauth_token( client_name=client_name, service_name=service_name, user_id=user_id ) if not ret: raise HandlerError("Revocation failed", 404) self.write(request, {"success": "Revocation successful"}) self.finish(request) except HandlerError as exc: self.write(request, error_format(exc.message), exc.code) self.finish(request)
def delete_auth(self, request): try: guid = self.name authz = yield Authz(uuid=guid).get_token(self.application.db) if not authz: raise HandlerError("guid '%s' not found" % guid, 404) # Grab the service. service = self.get_service(authz.service_name, request) # Delete the token and call the revocation method. data_ret = yield defer.gatherResults( [ service.revoke_authorization(authorization={"token": authz.token}), self.application.db.delete_user_data( uuid=guid, user_id=authz.user_id, service_name=authz.service_name ), ] ) ret = yield self.application.db.delete_oauth_token(uuid=guid) if not ret or not data_ret: raise HandlerError("Revocation failed", 404) self.write(request, {"success": "Revocation successful"}) self.finish(request) except HandlerError as exc: self.write(request, error_format(exc.message), exc.code) self.finish(request)
def test_post_unauthorized_bad_guid(self): return self.post_and_verify( path='/api/loopback/sleep_post', args=dict(guid='asdf'), response_code=401, result=error_format("User not authorized"), )
def test_delete_not_found(self): # Confirm that attempting to delete return self.delete_and_verify( path='/auth/not_found', response_code=404, result=error_format("guid 'not_found' not found"), )
def test_get_view_multiple_guids_same_guid(self): yield self.post_and_verify( path="/view", args=json.dumps( dict( name="foo", definition=[ {"service": "loopback", "method": "num_foo"}, {"service": "loopback", "method": "random"}, ], ) ), response_code=200, result={"success": "View 'foo' created"}, ) yield self.set_authorization(user_id="1234") uuid_one = self.uuid yield self.get_and_verify( path="/view/foo/invoke", args=dict(guid=[uuid_one, uuid_one]), response_code=400, result=error_format("Duplicate GUIDs '%s' provided" % uuid_one), )
def test_unsupported_url_json_response_delete(self): return self.delete_and_verify( path='/does/not/exist', response_code=404, response_type='application/json', result=error_format("'/does/not/exist' not found"), )
def _test_post_error(self, args, error): return self.post_and_verify( path='/status', args=args, response_code=400, result=error_format(error), )
def test_get_bad_service(self): yield self.set_authorization(user_id="2345") yield self.get_and_verify( path="/stream", args=dict(guid=self.uuid, service="no_such_service"), response_code=404, result=error_format("Service 'no_such_service' unknown"), )
def test_post_guid_for_wrong_service(self): yield self.set_authorization(client_name='testing2') yield self.post_and_verify( path='/data/loopback/time', args=dict(guid=self.uuid), response_code=400, result=error_format("Unknown GUID provided"), )
def test_get_bad_uuid_for_service(self): yield self.set_authorization() yield self.get_and_verify( path="/stream", args=dict(guid=self.uuid, service="loopback2"), response_code=400, result=error_format("Unknown GUIDs provided"), )
def test_get_bad_forward(self): yield self.set_authorization() yield self.get_and_verify( path="/stream", args=dict(guid=self.uuid, forward="a"), response_code=400, result=error_format("Bad value for 'forward'"), )
def test_post_guid_for_wrong_service(self): yield self.set_authorization(service_name='loopback2') yield self.post_and_verify( path='/api/loopback/sleep_post', args=dict(guid=self.uuid), response_code=401, result=error_format("User not authorized"), )
def finish_auth(self, request): try: if not self.get_arguments(request, "service"): raise HandlerError("Missing argument 'service'", 400) service_name = self.get_argument(request, "service") try: service = self.get_service(service_name, request) except KeyError: raise HandlerError("Service '%s' unknown" % service_name, 404) client_name = self.get_client_name(request) authorization = yield service.finish_authorization(client_name=client_name, args=self.arguments(request)) # Upon successful initial authz, enqueue the daemon for right away. if authorization.is_new and service.daemon_class.__name__ in daemons.DAEMONS and self.application.redis: try: from twistedpyres import ResQ resq = ResQ(redis=self.application.redis) for method in service.daemon_class._recurring: yield resq.enqueue_at( (datetime.now() + timedelta(seconds=1)), service.daemon_class, {"sql": service.datastore.config, "environment": service.environment}, authorization.uuid, method, redis=self.application.redis, ) except Exception as e: logging.error("Error trying to queue: %s" % e.message) raise e self.set_header(request, "Location", "/auth/%s" % authorization.uuid) self.write(request, {"guid": authorization.uuid}, status=201) self.finish(request) # Set account timestamp after we return a guid since this may be an # expensive API call. if authorization.is_new: if "account_created_timestamp" in service._methods["GET"]: try: yield service.account_created_timestamp(authorization=authorization) except Exception as e: logging.error("Error getting account timestamp: %s" % e) except LightningError as exc: self.write(request, exc.error_msg, exc.code) self.finish(request) except HandlerError as exc: self.write(request, error_format(exc.message), exc.code) self.finish(request) except Exception as exc: logging.error(exc) self.write_error(request, exc) self.finish(request)
def add_data(self, request): try: service_name = self.service_name method_name = self.method_name try: service = self.get_service(service_name, request) except KeyError: raise HandlerError("Service '%s' unknown" % service_name, 404) if not method_name in service.methods()['GET']: raise HandlerError("Method '%s' on service '%s' unknown" % (method_name, service_name), 404) guids = self.get_arguments(request, 'guid') if not len(guids): raise HandlerError("No GUID provided", 400) elif len(guids) > 1: raise HandlerError("Too many GUIDs provided", 400) authorization = yield Authz( uuid=guids[0], client_name=self.get_client_name(request), ).get_token(self.application.db) if not authorization: raise HandlerError("Unknown GUID provided", 400) values = self.get_arguments(request, 'value') if not len(values): raise HandlerError("No value provided", 400) elif len(values) > 1: raise HandlerError("Too many values provided", 400) timestamp = int(self.get_argument(request, 'timestamp', time.time())) try: yield service.write_new_value( authorization=authorization, method=method_name, data=json.loads(values[0]), timestamp=timestamp, ) except ValueError: raise HandlerError("Invalid JSON provided", 400) except KeyError: raise HandlerError("Wrong key for data provided", 400) self.write(request, {'success': '/%s/%s written' % (service_name, method_name)}) self.finish(request) except HandlerError as exc: self.write(request, error_format(exc.message), exc.code) self.finish(request) except Exception as exc: self.write_error(request, exc) self.finish(request)
def test_loopback_broken(self): yield self.set_authorization() yield self.get_and_verify( path='/api/loopback/broken', args=dict(guid=self.uuid), response_code=500, result=error_format('500 This method is intentionally broken'), )
def test_start_authorize_failure(self): return self.get_and_verify( path='/auth', args=dict( service='loopback', ), response_code=400, result=error_format("Missing arguments: 'redirect_uri', 'username'", code=400), )
def test_get_multiple_uuids_same_service(self): yield self.set_authorization(user_id="1234") uuid1 = self.uuid yield self.set_authorization(user_id="2345") yield self.get_and_verify( path="/stream", args=dict(guid=[uuid1, self.uuid]), response_code=400, result=error_format("Multiple GUIDs for one service provided"), )
def test_post_no_value(self): yield self.set_authorization() yield self.post_and_verify( path='/data/loopback/time', args=dict( guid=self.uuid, ), response_code=400, result=error_format("No value provided"), )
def test_post_wrong_key(self): yield self.set_authorization() yield self.post_and_verify( path='/data/loopback/time', args=dict( guid=self.uuid, value=json.dumps({'num': 100}), ), response_code=400, result=error_format("Wrong key for data provided"), )
def test_post_too_many_values(self): yield self.set_authorization() yield self.post_and_verify( path='/data/loopback/time', args=dict( guid=self.uuid, value=['a', 'b'], ), response_code=400, result=error_format("Too many values provided"), )
def write_error(self, request, exc, status=500): "Decorate the write_error() method with useful JSONification" if status == 404: error = "'%s' not found" % (self.request.path) elif status == 405: error = "'%s' not supported for '%s'" % ( self.request.method, self.request.path ) elif status == 500: email_message = [] if exc: # Prepare the items for email_message.append('%s - (REMOTE IP: %s) %s %s' % ( status, request.getClientIP(), request.method, request.uri )) email_message.append('HEADERS: ' + str(request.requestHeaders)) # (error_type, error, traceback) = kwargs['exception'].exc_info # append to the email message # email_message.append('ERROR TYPE: ' + str(error_type)) # email_message.append('STACK TRACE:\n' + ''.join(format_list(extract_tb(traceback)))) try: error = exc.log_message # This means it wasn't an HTTPError except AttributeError as e: error = str(exc) else: error = 'Internal Server Error' # add the specific email error to the top of the email email_message.insert(0, 'ERROR: ' + error) # send the email self.application.email.send( subject='ERROR: %s [%s %s]' % ( error, request.method, request.uri ), message='\n\n'.join(email_message) ) # write to log logging.error(error) request.setResponseCode(status) self.write(request, error_format(error), status)
def test_post_unauthorized_too_many_guids(self): yield self.set_authorization( user_id='a1234', token='some token', ) first_uuid = self.uuid yield self.set_authorization( user_id='b2345', token='some other token', ) yield self.post_and_verify( path='/api/loopback/sleep_post', args=dict(guid=[first_uuid, self.uuid]), response_code=401, result=error_format("Too many GUIDs"), )
def delete_view(self, request): try: name = self.view_name view = yield self.application.db.get_view(name) if not view: self.write(request, error_format("View '%s' unknown" % name), 404) else: yield self.application.db.delete_view(name) self.write(request, {'definition': view}) self.finish(request) except Exception as exc: self.write_error(request, exc) self.finish(request)
def invoke_post_method(self, request): try: service_name = self.service_name method_name = self.method_name try: service = self.get_service(service_name, request) except KeyError: raise HandlerError("Service '%s' unknown" % service_name, 404) if not method_name in service.methods()['POST']: raise HandlerError("Method '%s' on service '%s' unknown" % (method_name, service_name), 404) guids = self.get_arguments(request, 'guid') if not len(guids): raise HandlerError("User not authorized", 401) authorizations = yield Authz( uuid=guids, client_name=self.get_client_name(request), service_name=self.decode_client_servicename(service_name, request), ).get_token(self.application.db) if len(authorizations) > 1: raise HandlerError('Too many GUIDs', 401) elif len(authorizations) != 1: raise HandlerError('User not authorized', 401) ret = None try: ret = yield getattr(service, method_name)( authorization=authorizations[0], arguments=self.arguments(request), ) except LightningError as exc: raise HandlerError(exc.error_msg, exc.code) if ret: self.write(request, ret) else: self.write(request, {'success': None}) self.finish(request) except HandlerError as exc: self.write(request, error_format(exc.message), exc.code) self.finish(request) except Exception as exc: self.write_error(request, exc) self.finish(request)
def test_get_view_no_guid(self): yield self.post_and_verify( path="/view", args=json.dumps( dict( name="foo", definition=[ {"service": "loopback", "method": "num_foo"}, {"service": "loopback", "method": "random"}, ], ) ), response_code=200, result={"success": "View 'foo' created"}, ) yield self.get_and_verify( path="/view/foo/invoke", response_code=401, result=error_format("User not authorized") )
def show_auth(self, request): try: guid = self.name authz = yield Authz(uuid=guid).get_token(self.application.db) if not authz: raise HandlerError("guid '%s' not found" % guid, 404) self.write( request, { "service_name": authz.service_name, "user_id": authz.user_id, "account_created_timestamp": authz.account_created_timestamp, "guid": guid, }, ) self.finish(request) except HandlerError as exc: self.write(request, error_format(exc.message), exc.code) self.finish(request)
def render_GET(self, request): "Handle get requests" try: service_name = self.service_name try: service = self.get_service(service_name, request) except KeyError: raise HandlerError("Service '%s' unknown" % service_name, 404) methods = service.methods() or [] self.write(request, {'methods': methods}) except HandlerError as exc: self.write(request, error_format(exc.message), exc.code) except Exception as exc: self.write_error(request, exc) return ""