def init(self): '''This method is invoked in order to set all common dependencies for test cases.''' self._settings_facade = SettingsFacade() self.DEFAULT_CLIENT_ID = self._settings_facade.get( "oauth2_idp")["client_id"] self.TOKEN_VALIDITY = self._settings_facade.get( "access_token_validity")
def test_routes_loaders_ok(self): '''This test case makes sure routes loaders are configured correctly for each configuration.''' self._settings_facade = SettingsFacade() self.assertTrue("fantastico.routing_engine.dummy_routeloader.DummyRouteLoader" in \ self._settings_facade.get("routes_loaders")) self.assertTrue("fantastico.mvc.controller_registrator.ControllerRouteLoader" in \ self._settings_facade.get("routes_loaders"))
def init(self): '''This method is invoked before executing each test case. It generates an access_token which is authorized to access user api.''' self._settings_facade = SettingsFacade() self.DEFAULT_CLIENT_ID = self._settings_facade.get( "oauth2_idp")["client_id"] self._access_token = self._get_oauth2_token(self.DEFAULT_CLIENT_ID, self.DEFAULT_USER_ID, self.SCOPES)
def _get_db_conn(self): '''This method opens a db connection and returns it to for usage.''' settings = SettingsFacade() db_config = settings.get("database_config") conn_manager = mvc.init_dm_db_engine(db_config) db_conn = conn_manager.get_connection(-50) return (conn_manager, -50, db_conn)
def test_settings_ok(self): '''Test case that ensures settings are functional for each environment available.''' self._settings_facade = SettingsFacade() self.assertEqual(["fantastico.middleware.request_middleware.RequestMiddleware", "fantastico.middleware.model_session_middleware.ModelSessionMiddleware", "fantastico.middleware.routing_middleware.RoutingMiddleware", "fantastico.oauth2.middleware.exceptions_middleware.OAuth2ExceptionsMiddleware", "fantastico.oauth2.middleware.tokens_middleware.OAuth2TokensMiddleware"], self._settings_facade.get("installed_middleware")) self.assertEqual(["en_us"], self._settings_facade.get("supported_languages"))
class SettingsIntegration(FantasticoIntegrationTestCase): '''Test suite that ensures current settings for various environments remain stable.''' def init(self): self._settings_facade = None def test_settings_ok(self): '''Test case that ensures settings are functional for each environment available.''' self._settings_facade = SettingsFacade() self.assertEqual(["fantastico.middleware.request_middleware.RequestMiddleware", "fantastico.middleware.model_session_middleware.ModelSessionMiddleware", "fantastico.middleware.routing_middleware.RoutingMiddleware", "fantastico.oauth2.middleware.exceptions_middleware.OAuth2ExceptionsMiddleware", "fantastico.oauth2.middleware.tokens_middleware.OAuth2TokensMiddleware"], self._settings_facade.get("installed_middleware")) self.assertEqual(["en_us"], self._settings_facade.get("supported_languages")) def test_routes_loaders_ok(self): '''This test case makes sure routes loaders are configured correctly for each configuration.''' self._settings_facade = SettingsFacade() self.assertTrue("fantastico.routing_engine.dummy_routeloader.DummyRouteLoader" in \ self._settings_facade.get("routes_loaders")) self.assertTrue("fantastico.mvc.controller_registrator.ControllerRouteLoader" in \ self._settings_facade.get("routes_loaders")) def test_database_config_ok(self): '''This test case ensures we have a database configured for fantastico framework.''' self._settings_facade = SettingsFacade() expected_config = {"drivername": "mysql+mysqlconnector", "username": "******", "password": "******", "port": 3306, "database": "fantastico"} config = self._settings_facade.get("database_config") self.assertEqual(expected_config["drivername"], config["drivername"]) self.assertEqual(expected_config["username"], config["username"]) self.assertEqual(expected_config["password"], config["password"]) self.assertEqual(expected_config["port"], config["port"]) self.assertEqual(expected_config["database"], config["database"]) self.assertEqual("utf8", config["additional_params"]["charset"])
def init(self): '''This method is invoked before executing each test case. It generates an access_token which is authorized to access user api.''' self._settings_facade = SettingsFacade() self.DEFAULT_CLIENT_ID = self._settings_facade.get("oauth2_idp")["client_id"] self._access_token = self._get_oauth2_token(self.DEFAULT_CLIENT_ID, self.DEFAULT_USER_ID, self.SCOPES)
def init(self): '''This method is invoked in order to set all common dependencies for test cases.''' self._settings_facade = SettingsFacade() self.DEFAULT_CLIENT_ID = self._settings_facade.get("oauth2_idp")["client_id"] self.TOKEN_VALIDITY = self._settings_facade.get("access_token_validity")
def _build_context(self, request): '''Method used to build the context object starting for a request.''' client_langs = request.accept_language settings_facade = SettingsFacade(request.environ) supported_langs = settings_facade.get("supported_languages") if hasattr(client_langs, "_parsed"): client_langs = client_langs._parsed # pylint: disable=W0212 else: client_langs = [] context = RequestContext(settings_facade, self._get_supported_lang(supported_langs, client_langs)) request.context = context
def exec(self, os_lib=os): '''This method is executed to activate the given extension name.''' comp_name = self._arguments.name contrib_path = instantiator.get_package_abslocation(contrib) comp_path = "%s%s" % (contrib_path, comp_name) if not os_lib.path.exists(comp_path): raise FantasticoSdkCommandNotFoundError( "Extension %s does not exist." % comp_name) file_matcher = lambda abspath, filename: True root_folder = instantiator.get_component_path_data( SettingsFacade().get_config().__class__)[1] comp_root_folder = "%s%s/%s" % (root_folder, self._arguments.comp_root, comp_name) if not os_lib.path.exists(comp_root_folder): os_lib.mkdir(comp_root_folder) instantiator.scan_folder_by_criteria( comp_path, file_matcher, action=lambda abspath, filename: \ self._link_files(abspath, filename, contrib_path, root_folder, os_lib), os_lib=os_lib)
def _exec_command_help_scenario(self, argv, assert_action, cmd_name): '''This method defines a template for testing subcommands --help option. Once the subcommand is executed, the help string is captured and passed to assert action. .. code-block:: python class SampleSubcommandTest(CommandBaseIntegration): def test_sample_help(self): def assert_action(help_str): self.help_str.startswith("usage: %s" % SampleSubcommand.get_help()) self._exec_command_help_scenario(["fantastico", "sample"], "sample")''' if argv[-1] != "--help" or "-h": argv.append("--help") with self.assertRaises(SystemExit): main(argv) help_str = self._stdout.getvalue() assert_action(help_str) expected_doc_link = "See: %sfeatures/sdk/command_%s.html" % ( SettingsFacade().get("doc_base"), cmd_name.replace("-", "_")) self.assertGreater(help_str.find(expected_doc_link), -1)
def test_upload_file_ok(self): '''This test case ensure response is retrieved correctly from upload file.''' controller = RoutesForControllerTesting(SettingsFacade()) response = controller.upload_file(Mock()) self.assertIsNotNone(response) self.assertTrue(response.text.find("Hello world.") > -1)
def register_resources(self, path): '''This method scans all files and folders from the given path, match the filenames against registered file patterns and import all ROA resources.''' instantiator.import_modules_from_folder( path, file_matcher=self._is_file_supported, settings_facade=SettingsFacade())
def test_database_config_ok(self): '''This test case ensures we have a database configured for fantastico framework.''' self._settings_facade = SettingsFacade() expected_config = {"drivername": "mysql+mysqlconnector", "username": "******", "password": "******", "port": 3306, "database": "fantastico"} config = self._settings_facade.get("database_config") self.assertEqual(expected_config["drivername"], config["drivername"]) self.assertEqual(expected_config["username"], config["username"]) self.assertEqual(expected_config["password"], config["password"]) self.assertEqual(expected_config["port"], config["port"]) self.assertEqual(expected_config["database"], config["database"]) self.assertEqual("utf8", config["additional_params"]["charset"])
def setUp(self): self.__envs = [("fantastico.settings.BasicSettings", BasicSettings), ("fantastico.settings.AwsStageSettings", AwsStageSettings)] self.__old_middlewares_call = [] self._save_call_methods( SettingsFacade().get_config().installed_middleware) super(FantasticoIntegrationTestCase, self).setUp()
def setup_once(cls): '''This method is invoked once before executing any test case. For model facade we create a series of messages.''' cls.settings_facade = SettingsFacade() mvc.CONN_MANAGER = mvc.init_dm_db_engine( cls.settings_facade.get("database_config"), echo=True) cls.model_facade = ModelFacade( ModelFacadeMessage, mvc.CONN_MANAGER.get_connection(ModelFacadeIntegration.request_id))
class BaseModelIntegration(FantasticoIntegrationTestCase): '''This class provides the test cases that ensures sql alchemy configuration works as expected.''' def init(self): self._settings_facade = SettingsFacade() def test_connection_ok(self): '''This test case execute a simple sanity check against database configuration.''' db_config = self._settings_facade.get("database_config") from fantastico import mvc mvc.CONN_MANAGER = mvc.init_dm_db_engine(db_config) self.assertIsNotNone(mvc.BASEMODEL) self.assertIsNotNone(mvc.CONN_MANAGER) session = mvc.CONN_MANAGER.get_connection(uuid.uuid4()) session.execute("SELECT 1")
def test_context_initialization(self): '''This test case ensures context is correctly initialized.''' settings_cls = SettingsFacade().get_config().__class__ self._middleware(self._environ, Mock()) request = self._environ.get("fantastico.request") self.assertIsNotNone(request) self.assertIsNotNone(request.request_id) self.assertEqual("GET", request.method) self.assertEqual("application/json", request.content_type) self.assertEqual("123", request.headers.get("oauth_bearer")) self.assertEqual(1, int(request.params.get("id"))) self.assertEqual(settings_cls().installed_middleware, request.context.settings.get("installed_middleware")) self.assertEqual(settings_cls().supported_languages, request.context.settings.get("supported_languages")) self.assertEqual("en_us", request.context.language.code)
def init(self): self._settings_facade = SettingsFacade()
def init(self): '''This method is responsible for initializing a new connection manager before each test case.''' mvc.CONN_MANAGER = mvc.init_dm_db_engine( db_config=SettingsFacade().get("database_config"), echo=True)
def test_subroute_ok(self): '''This test case ensure method is correctly executed from SubroutesController.''' controller = SubroutesController(SettingsFacade()) self.assertRaises(FantasticoTemplateNotFoundError, controller.handle_route, *[Mock()])
def init(self): self._environ = {} self._settings = SettingsFacade(self._environ)
class TestSettingsFacadeSuite(FantasticoUnitTestsCase): '''Test suite for settings facade functionality.''' def init(self): self._environ = {} self._settings = SettingsFacade(self._environ) def test_get_config_noenv(self): '''Test case that checks a settings class can be obtained even if FANTASTICO_ACTIVE_CONFIG environment variable is not found.''' self.assertIsInstance(self._settings.get_config(), BasicSettings) def test_get_config_env_notfound(self): '''Test case that checks an exception is thrown when the specified class config can not be resolved.''' self._environ["FANTASTICO_ACTIVE_CONFIG"] = "not.found.class" self.assertRaises(FantasticoClassNotFoundError, self._settings.get_config) def test_get_config_env_found(self): '''Test case that checks that a custom configuration is instantiated correctly.''' self._environ["FANTASTICO_ACTIVE_CONFIG"] = "fantastico.tests.test_settings.SampleSettings" self.assertIsInstance(self._settings.get_config(), SampleSettings) def test_get_setting(self): '''Test case that checks settings are correctly retrieved from the active configuration. In addition it ensures the configuration contains the minimum amount of information for installed_middleware.''' installed_middleware = self._settings.get("installed_middleware") self.assertGreaterEqual(len(installed_middleware), 3) self.assertEqual(["fantastico.middleware.request_middleware.RequestMiddleware", "fantastico.middleware.model_session_middleware.ModelSessionMiddleware", "fantastico.middleware.routing_middleware.RoutingMiddleware", "fantastico.oauth2.middleware.exceptions_middleware.OAuth2ExceptionsMiddleware", "fantastico.oauth2.middleware.tokens_middleware.OAuth2TokensMiddleware"], installed_middleware) def test_get_setting_unavailable(self): '''Test case that ensures an exception is thrown whenever we try to get a setting that is not available.''' self.assertRaises(FantasticoSettingNotFoundError, self._settings.get, *["setting_not_found"]) def test_get_setting_internal_error(self): '''Test case that ensures properties internal server errors are handled gracefully.''' self._environ["FANTASTICO_ACTIVE_CONFIG"] = "fantastico.tests.test_settings.SampleSettings" self.assertRaises(FantasticoSettingNotFoundError, self._settings.get, *["installed_middleware"]) def test_get_root_folder(self): '''Test case that ensures get root folder works correctly.''' self._environ["FANTASTICO_ACTIVE_CONFIG"] = "fantastico.settings.BasicSettings" expected_root = self._get_root_folder() root_folder = self._settings.get_root_folder() self.assertEqual(expected_root, root_folder)
class OAuth2ControllerIntegrationTests(DevServerIntegration): '''This class provides the integration tests suite for ensuring oauth2 controller works as expected.''' DEFAULT_CLIENT_ID = None DEFAULT_USER_ID = 1 DEFAULT_SCOPES = "user.profile.read user.profile.update user.profile.delete" TOKEN_VALIDITY = None _settings_facade = None def init(self): '''This method is invoked in order to set all common dependencies for test cases.''' self._settings_facade = SettingsFacade() self.DEFAULT_CLIENT_ID = self._settings_facade.get( "oauth2_idp")["client_id"] self.TOKEN_VALIDITY = self._settings_facade.get( "access_token_validity") def test_implicit_grant(self): '''This test case ensures implicit grant type success scenario works as expected.''' state = "abcd xyz&abc" redirect_uri = "/oauth/idp/ui/cb" login_token = self._get_oauth2_logintoken(self.DEFAULT_CLIENT_ID, self.DEFAULT_USER_ID) endpoint = "/oauth/authorize?response_type=token&client_id=%s&login_token=%s&state=%s&scope=%s&redirect_uri=%s" % \ (self.DEFAULT_CLIENT_ID, login_token, urllib.parse.quote(state), urllib.parse.quote(self.DEFAULT_SCOPES), urllib.parse.quote(redirect_uri)) results = {} def trigger_implicit_flow(server): http_conn = http.client.HTTPConnection(host=server.hostname, port=server.port) http_conn.request("GET", endpoint) results["response"] = http_conn.getresponse() http_conn.close() def assert_success(server): '''This method ensures the flow completed successfully by checking the location and hash part of the location.''' response = results["response"] self.assertIsNotNone(response) self.assertEqual(302, response.status) location = response.headers["Location"] self.assertIsNotNone(location) self.assertTrue( location.startswith("%s#access_token=" % redirect_uri)) self.assertTrue( location.find("&expires_in=%s" % self.TOKEN_VALIDITY) > -1) self.assertTrue( location.find("&state=%s" % urllib.parse.quote(state)) > -1) self.assertTrue( location.find("&scope=%s" % urllib.parse.quote(self.DEFAULT_SCOPES)) > -1) location = location.replace("%s#access_token=" % redirect_uri, "") access_token = location[:location.find("&")] self.assertGreater(len(access_token), 400) self._run_test_against_dev_server(trigger_implicit_flow, assert_success) def test_authorize_implicit_missing_param_form(self): '''This test case ensures error, error_uri and error_description query parameters are appended to redirect_uri passed for implicit grant type.''' self._test_error_handling_implicit_graceful("form", "?") def test_authorize_implicit_missing_param_hash(self): '''This test case ensures error, error_uri and error_description query parameters are appended to redirect_uri passed for implicit grant type (into hash section).''' self._test_error_handling_implicit_graceful("hash", "#") def _test_error_handling_implicit_graceful(self, format, delimiter): '''This method provides a template for ensuring error handling method types which describe error through redirect and query or hash strings work as expected.''' endpoint = "/oauth/authorize?error_format=%s&redirect_uri=/example/cb" % format results = {} def invoke_implicit(server): '''This method tries to retrieve login screen.''' http_conn = http.client.HTTPConnection(server.hostname, server.port) http_conn.request("GET", endpoint) results["response"] = http_conn.getresponse() http_conn.close() def assert_error_graceful(server): '''This method asserts that error was gracefully handled by OAuth2 exceptions middleware.''' response = results.get("response") self.assertIsNotNone(response) self.assertEqual(302, response.status) location = response.headers.get("Location") self.assertIsNotNone(location) self.assertTrue(location.startswith("/example/cb%s" % delimiter)) self.assertTrue( location.find("error=invalid_request") > -1, "%s does not contain error query param." % location) result = re.findall(r"error_description=(.*)&", location) self.assertEqual( 1, len(result), "%s does not contain error_description query param." % location) result = re.findall(r"error_uri=(.*)", location) self.assertEqual( 1, len(result), "%s does not contain error_uri query param." % location) self._run_test_against_dev_server(invoke_implicit, assert_error_graceful)
class UserResourceIntegrationTests(DevServerIntegration): '''This class provides the integration tests which ensures that CRUD operations work as expected on user resource. We do this because we also want to validate oauth2 roa validation.''' DEFAULT_CLIENT_ID = None DEFAULT_USER_ID = 1 SCOPES = "user.profile.read user.profile.update user.profile.delete" _access_token = None _settings_facade = None def init(self): '''This method is invoked before executing each test case. It generates an access_token which is authorized to access user api.''' self._settings_facade = SettingsFacade() self.DEFAULT_CLIENT_ID = self._settings_facade.get( "oauth2_idp")["client_id"] self._access_token = self._get_oauth2_token(self.DEFAULT_CLIENT_ID, self.DEFAULT_USER_ID, self.SCOPES) def cleanup(self): self._invalidate_oauth2_token(self._access_token) self._access_token = None def _create_user(self, hostname, port): '''This method simply creates a new user using the api and returns the new user username.''' username = "******" endpoint = "/api/latest/oauth-idp-profile" user = {"username": username, "password": "******"} http_conn = http.client.HTTPConnection(hostname, port) http_conn.request("POST", endpoint, json.dumps(user)) response = http_conn.getresponse() self.assertIsNotNone(response) self.assertEqual(201, response.status) location = response.headers.get("Location") self.assertIsNotNone(location) http_conn.close() return location.split("/")[-1] def _delete_user(self, user_id, token, hostname, port): '''This method removes the requested user_id.''' db_conn = None conn_manager = None request_id = None person_facade = None person = None try: conn_manager, request_id, db_conn = self._get_db_conn() person_facade = ModelFacade(Person, db_conn) user = ModelFacade(User, db_conn).find_by_pk({User.user_id: user_id}) person = person_facade.find_by_pk( {Person.person_id: user.person_id}) endpoint = "/api/latest/oauth-idp-profile/%s?token=%s" % (user_id, token) http_conn = http.client.HTTPConnection(hostname, port) http_conn.request("DELETE", endpoint) response = http_conn.getresponse() http_conn.close() self.assertIsNotNone(response) self.assertEqual(204, response.status) finally: if person: person_facade.delete(person) if db_conn: conn_manager.close_connection(request_id) def test_retrieve_user_ok(self): '''This test case ensures default user used in tests can be retrieved correctly.''' results = {"user_id": None, "token": None} def get_new_user(server): '''This method triggers an http get on default user.''' results["user_id"] = user_id = int( self._create_user(server.hostname, server.port)) results["token"] = access_token = self._get_oauth2_token( self.DEFAULT_CLIENT_ID, user_id, self.SCOPES) http_conn = http.client.HTTPConnection(host=server.hostname, port=server.port) http_conn.request( "GET", "/api/latest/oauth-idp-profile/%s" % user_id, headers={"Authorization": "Bearer %s" % access_token}) results["response"] = http_conn.getresponse() http_conn.close() self._delete_user(user_id, access_token, server.hostname, server.port) def assert_user(server): '''This method assert the user response against expected results.''' response = results.get("response") self.assertIsNotNone(response) self.assertEqual(200, response.status) body = response.read() self.assertIsNotNone(body) body = json.loads(body.decode()) self.assertNotIn("password", body) self.assertEqual("*****@*****.**", body["username"]) self.assertEqual(results.get("user_id"), body["user_id"]) self.assertGreater(body["person_id"], 1) self._run_test_against_dev_server(get_new_user, assert_user) def test_retrieve_users_ok(self): '''This test case ensures users can be retrieved .''' results = {} def get_default_user(server): '''This method triggers an http get on default users collection endpoint.''' http_conn = http.client.HTTPConnection(host=server.hostname, port=server.port) http_conn.request( "GET", "/api/latest/oauth-idp-profile?token=%s" % self._access_token) results["response"] = http_conn.getresponse() http_conn.close() def assert_user(server): '''This method assert the user response against expected results.''' response = results.get("response") self.assertIsNotNone(response) self.assertEqual(200, response.status) body = response.read() self.assertIsNotNone(body) body = json.loads(body.decode()) items = body.get("items") self.assertNotIn("password", items[0]) self.assertEqual("*****@*****.**", items[0]["username"]) self.assertEqual(1, items[0]["user_id"]) self.assertEqual(1, items[0]["person_id"]) self._run_test_against_dev_server(get_default_user, assert_user) def test_get_users_unauthorized(self): '''This method ensures users collection can not be accessed without an access token sent.''' results = {} def get_users(server): '''This method triggers an http get on default user.''' http_conn = http.client.HTTPConnection(host=server.hostname, port=server.port) http_conn.request("GET", "/api/latest/oauth-idp-profile") results["response"] = http_conn.getresponse() http_conn.close() def assert_unauthorized(server): '''This method assert the user response against expected results.''' response = results.get("response") self.assertIsNotNone(response) self.assertEqual(401, response.status) self._run_test_against_dev_server(get_users, assert_unauthorized)
class OAuth2ControllerIntegrationTests(DevServerIntegration): '''This class provides the integration tests suite for ensuring oauth2 controller works as expected.''' DEFAULT_CLIENT_ID = None DEFAULT_USER_ID = 1 DEFAULT_SCOPES = "user.profile.read user.profile.update user.profile.delete" TOKEN_VALIDITY = None _settings_facade = None def init(self): '''This method is invoked in order to set all common dependencies for test cases.''' self._settings_facade = SettingsFacade() self.DEFAULT_CLIENT_ID = self._settings_facade.get("oauth2_idp")["client_id"] self.TOKEN_VALIDITY = self._settings_facade.get("access_token_validity") def test_implicit_grant(self): '''This test case ensures implicit grant type success scenario works as expected.''' state = "abcd xyz&abc" redirect_uri = "/oauth/idp/ui/cb" login_token = self._get_oauth2_logintoken(self.DEFAULT_CLIENT_ID, self.DEFAULT_USER_ID) endpoint = "/oauth/authorize?response_type=token&client_id=%s&login_token=%s&state=%s&scope=%s&redirect_uri=%s" % \ (self.DEFAULT_CLIENT_ID, login_token, urllib.parse.quote(state), urllib.parse.quote(self.DEFAULT_SCOPES), urllib.parse.quote(redirect_uri)) results = {} def trigger_implicit_flow(server): http_conn = http.client.HTTPConnection(host=server.hostname, port=server.port) http_conn.request("GET", endpoint) results["response"] = http_conn.getresponse() http_conn.close() def assert_success(server): '''This method ensures the flow completed successfully by checking the location and hash part of the location.''' response = results["response"] self.assertIsNotNone(response) self.assertEqual(302, response.status) location = response.headers["Location"] self.assertIsNotNone(location) self.assertTrue(location.startswith("%s#access_token=" % redirect_uri)) self.assertTrue(location.find("&expires_in=%s" % self.TOKEN_VALIDITY) > -1) self.assertTrue(location.find("&state=%s" % urllib.parse.quote(state)) > -1) self.assertTrue(location.find("&scope=%s" % urllib.parse.quote(self.DEFAULT_SCOPES)) > -1) location = location.replace("%s#access_token=" % redirect_uri, "") access_token = location[:location.find("&")] self.assertGreater(len(access_token), 400) self._run_test_against_dev_server(trigger_implicit_flow, assert_success) def test_authorize_implicit_missing_param_form(self): '''This test case ensures error, error_uri and error_description query parameters are appended to redirect_uri passed for implicit grant type.''' self._test_error_handling_implicit_graceful("form", "?") def test_authorize_implicit_missing_param_hash(self): '''This test case ensures error, error_uri and error_description query parameters are appended to redirect_uri passed for implicit grant type (into hash section).''' self._test_error_handling_implicit_graceful("hash", "#") def _test_error_handling_implicit_graceful(self, format, delimiter): '''This method provides a template for ensuring error handling method types which describe error through redirect and query or hash strings work as expected.''' endpoint = "/oauth/authorize?error_format=%s&redirect_uri=/example/cb" % format results = {} def invoke_implicit(server): '''This method tries to retrieve login screen.''' http_conn = http.client.HTTPConnection(server.hostname, server.port) http_conn.request("GET", endpoint) results["response"] = http_conn.getresponse() http_conn.close() def assert_error_graceful(server): '''This method asserts that error was gracefully handled by OAuth2 exceptions middleware.''' response = results.get("response") self.assertIsNotNone(response) self.assertEqual(302, response.status) location = response.headers.get("Location") self.assertIsNotNone(location) self.assertTrue(location.startswith("/example/cb%s" % delimiter)) self.assertTrue(location.find("error=invalid_request") > -1, "%s does not contain error query param." % location) result = re.findall(r"error_description=(.*)&", location) self.assertEqual(1, len(result), "%s does not contain error_description query param." % location) result = re.findall(r"error_uri=(.*)", location) self.assertEqual(1, len(result), "%s does not contain error_uri query param." % location) self._run_test_against_dev_server(invoke_implicit, assert_error_graceful)
class UserResourceIntegrationTests(DevServerIntegration): '''This class provides the integration tests which ensures that CRUD operations work as expected on user resource. We do this because we also want to validate oauth2 roa validation.''' DEFAULT_CLIENT_ID = None DEFAULT_USER_ID = 1 SCOPES = "user.profile.read user.profile.update user.profile.delete" _access_token = None _settings_facade = None def init(self): '''This method is invoked before executing each test case. It generates an access_token which is authorized to access user api.''' self._settings_facade = SettingsFacade() self.DEFAULT_CLIENT_ID = self._settings_facade.get("oauth2_idp")["client_id"] self._access_token = self._get_oauth2_token(self.DEFAULT_CLIENT_ID, self.DEFAULT_USER_ID, self.SCOPES) def cleanup(self): self._invalidate_oauth2_token(self._access_token) self._access_token = None def _create_user(self, hostname, port): '''This method simply creates a new user using the api and returns the new user username.''' username = "******" endpoint = "/api/latest/oauth-idp-profile" user = {"username": username, "password": "******"} http_conn = http.client.HTTPConnection(hostname, port) http_conn.request("POST", endpoint, json.dumps(user)) response = http_conn.getresponse() self.assertIsNotNone(response) self.assertEqual(201, response.status) location = response.headers.get("Location") self.assertIsNotNone(location) http_conn.close() return location.split("/")[-1] def _delete_user(self, user_id, token, hostname, port): '''This method removes the requested user_id.''' db_conn = None conn_manager = None request_id = None person_facade = None person = None try: conn_manager, request_id, db_conn = self._get_db_conn() person_facade = ModelFacade(Person, db_conn) user = ModelFacade(User, db_conn).find_by_pk({User.user_id: user_id}) person = person_facade.find_by_pk({Person.person_id: user.person_id}) endpoint = "/api/latest/oauth-idp-profile/%s?token=%s" % (user_id, token) http_conn = http.client.HTTPConnection(hostname, port) http_conn.request("DELETE", endpoint) response = http_conn.getresponse() http_conn.close() self.assertIsNotNone(response) self.assertEqual(204, response.status) finally: if person: person_facade.delete(person) if db_conn: conn_manager.close_connection(request_id) def test_retrieve_user_ok(self): '''This test case ensures default user used in tests can be retrieved correctly.''' results = {"user_id": None, "token": None} def get_new_user(server): '''This method triggers an http get on default user.''' results["user_id"] = user_id = int(self._create_user(server.hostname, server.port)) results["token"] = access_token = self._get_oauth2_token(self.DEFAULT_CLIENT_ID, user_id, self.SCOPES) http_conn = http.client.HTTPConnection(host=server.hostname, port=server.port) http_conn.request("GET", "/api/latest/oauth-idp-profile/%s" % user_id, headers={"Authorization": "Bearer %s" % access_token}) results["response"] = http_conn.getresponse() http_conn.close() self._delete_user(user_id, access_token, server.hostname, server.port) def assert_user(server): '''This method assert the user response against expected results.''' response = results.get("response") self.assertIsNotNone(response) self.assertEqual(200, response.status) body = response.read() self.assertIsNotNone(body) body = json.loads(body.decode()) self.assertNotIn("password", body) self.assertEqual("*****@*****.**", body["username"]) self.assertEqual(results.get("user_id"), body["user_id"]) self.assertGreater(body["person_id"], 1) self._run_test_against_dev_server(get_new_user, assert_user) def test_retrieve_users_ok(self): '''This test case ensures users can be retrieved .''' results = {} def get_default_user(server): '''This method triggers an http get on default users collection endpoint.''' http_conn = http.client.HTTPConnection(host=server.hostname, port=server.port) http_conn.request("GET", "/api/latest/oauth-idp-profile?token=%s" % self._access_token) results["response"] = http_conn.getresponse() http_conn.close() def assert_user(server): '''This method assert the user response against expected results.''' response = results.get("response") self.assertIsNotNone(response) self.assertEqual(200, response.status) body = response.read() self.assertIsNotNone(body) body = json.loads(body.decode()) items = body.get("items") self.assertNotIn("password", items[0]) self.assertEqual("*****@*****.**", items[0]["username"]) self.assertEqual(1, items[0]["user_id"]) self.assertEqual(1, items[0]["person_id"]) self._run_test_against_dev_server(get_default_user, assert_user) def test_get_users_unauthorized(self): '''This method ensures users collection can not be accessed without an access token sent.''' results = {} def get_users(server): '''This method triggers an http get on default user.''' http_conn = http.client.HTTPConnection(host=server.hostname, port=server.port) http_conn.request("GET", "/api/latest/oauth-idp-profile") results["response"] = http_conn.getresponse() http_conn.close() def assert_unauthorized(server): '''This method assert the user response against expected results.''' response = results.get("response") self.assertIsNotNone(response) self.assertEqual(401, response.status) self._run_test_against_dev_server(get_users, assert_unauthorized)
class TestSettingsFacadeSuite(FantasticoUnitTestsCase): '''Test suite for settings facade functionality.''' def init(self): self._environ = {} self._settings = SettingsFacade(self._environ) def test_get_config_noenv(self): '''Test case that checks a settings class can be obtained even if FANTASTICO_ACTIVE_CONFIG environment variable is not found.''' self.assertIsInstance(self._settings.get_config(), BasicSettings) def test_get_config_env_notfound(self): '''Test case that checks an exception is thrown when the specified class config can not be resolved.''' self._environ["FANTASTICO_ACTIVE_CONFIG"] = "not.found.class" self.assertRaises(FantasticoClassNotFoundError, self._settings.get_config) def test_get_config_env_found(self): '''Test case that checks that a custom configuration is instantiated correctly.''' self._environ[ "FANTASTICO_ACTIVE_CONFIG"] = "fantastico.tests.test_settings.SampleSettings" self.assertIsInstance(self._settings.get_config(), SampleSettings) def test_get_setting(self): '''Test case that checks settings are correctly retrieved from the active configuration. In addition it ensures the configuration contains the minimum amount of information for installed_middleware.''' installed_middleware = self._settings.get("installed_middleware") self.assertGreaterEqual(len(installed_middleware), 3) self.assertEqual([ "fantastico.middleware.request_middleware.RequestMiddleware", "fantastico.middleware.model_session_middleware.ModelSessionMiddleware", "fantastico.middleware.routing_middleware.RoutingMiddleware", "fantastico.oauth2.middleware.exceptions_middleware.OAuth2ExceptionsMiddleware", "fantastico.oauth2.middleware.tokens_middleware.OAuth2TokensMiddleware" ], installed_middleware) def test_get_setting_unavailable(self): '''Test case that ensures an exception is thrown whenever we try to get a setting that is not available.''' self.assertRaises(FantasticoSettingNotFoundError, self._settings.get, *["setting_not_found"]) def test_get_setting_internal_error(self): '''Test case that ensures properties internal server errors are handled gracefully.''' self._environ[ "FANTASTICO_ACTIVE_CONFIG"] = "fantastico.tests.test_settings.SampleSettings" self.assertRaises(FantasticoSettingNotFoundError, self._settings.get, *["installed_middleware"]) def test_get_root_folder(self): '''Test case that ensures get root folder works correctly.''' self._environ[ "FANTASTICO_ACTIVE_CONFIG"] = "fantastico.settings.BasicSettings" expected_root = self._get_root_folder() root_folder = self._settings.get_root_folder() self.assertEqual(expected_root, root_folder)
class RoaController(BaseController): '''This class provides dynamic routes for ROA registered resources. All CRUD operations are supported out of the box. In addition error handling is automatically provided by this controller.''' SETTINGS_FACADE = SettingsFacade() ROA_API = roa_helper.normalize_absolute_roa_uri( SETTINGS_FACADE.get("roa_api")) BASE_URL = r"%s/(?P<version>\d{1,}\.\d{1,})(?P<resource_url>/[^/]*?)" % ROA_API BASE_LATEST_URL = "%s/latest(?P<resource_url>/[^/]*?)" % ROA_API OFFSET_DEFAULT = 0 LIMIT_DEFAULT = 100 def __init__(self, settings_facade, resources_registry_cls=ResourcesRegistry, model_facade_cls=ModelFacade, conn_manager=mvc, json_serializer_cls=ResourceJsonSerializer, query_parser_cls=QueryParser): super(RoaController, self).__init__(settings_facade) self._resources_registry = resources_registry_cls() self._model_facade_cls = model_facade_cls self._conn_manager = conn_manager self._json_serializer_cls = json_serializer_cls self._query_parser_cls = query_parser_cls doc_base = "%sfeatures/roa/errors/" % self._settings_facade.get( "doc_base") self._errors_url = doc_base + "error_%s.html" self._roa_api = self._settings_facade.get("roa_api") def _parse_filter(self, filter_expr, model): '''This method parse a string filter expression and builds a compatible ModelFilter.''' if not filter_expr: return None query_parser = self._query_parser_cls() return query_parser.parse_filter(filter_expr, model) def _parse_sort(self, sort_expr, model): '''This method parse a string sort expression and builds a compatible ModelSort.''' if not sort_expr: return None query_parser = self._query_parser_cls() return query_parser.parse_sort(sort_expr, model) def _build_error_response(self, http_code, error_code, error_description, error_details): '''This method builds an error response compliant with :doc:`/features/roa/rest_responses` specification.''' error = { "error_code": error_code, "error_description": error_description, "error_details": error_details } response = Response(text=json.dumps(error), status_code=http_code, content_type="application/json") self._add_cors_headers(response) return response def _handle_resource_notfound(self, version, url): '''This method build a resource not found response which is sent to the client. You can find more information about error responses format on :doc:`/features/roa/rest_responses`''' error_code = 10000 return self._build_error_response( http_code=404, error_code=error_code, error_description="Resource %s version %s does not exist." % (url, version), error_details=self._errors_url % error_code) def _handle_resource_item_notfound(self, version, url, resource_id): '''This method build a resource not found response which is sent to the client. You can find more information about error responses format on :doc:`/features/roa/rest_responses`. In the error description it also contains resource id.''' error_code = 10040 return self._build_error_response(http_code=404, error_code=error_code, error_description="Resource %s version %s id %s does not exist." % \ (url, version, resource_id), error_details=self._errors_url % error_code) def _handle_resource_invalid(self, version, url, ex): '''This method builds a resource invalid response which is sent to the client.''' error_code = 10010 return self._build_error_response(http_code=ex.http_code, error_code=error_code, error_description="Resource %s version %s is invalid: %s" % \ (url, version, str(ex)), error_details=self._errors_url % error_code) def _handle_resource_nobody(self, version, url): '''This method builds a resource nobody given response which is sent to the client.''' error_code = 10020 return self._build_error_response(http_code=400, error_code=error_code, error_description="Resource %s version %s can not be created: no body given." % \ (url, version), error_details=self._errors_url % error_code) def _handle_resource_dberror(self, version, url, dbex): '''This method builds a resource dberror response which is sent to the client.''' error_code = 10030 return self._build_error_response(http_code=400, error_code=error_code, error_description="Resource %s version %s db exception: %s." % \ (url, version, str(dbex)), error_details=self._errors_url % error_code) def _get_current_connection(self, request): '''This method returns the current db connection for this request.''' return self._conn_manager.CONN_MANAGER.get_connection( request.request_id) def _trim_resource_url(self, resource_url): '''This method removes traling slash from resource url.''' if resource_url[-1] == "/": resource_url = resource_url[:-1] if resource_url[0] != "/": resource_url = "/%s" % resource_url return resource_url def _add_cors_headers(self, response): '''This method add cors headers to a given respones.''' response.headers["Access-Control-Allow-Origin"] = "*" response.headers[ "Access-Control-Allow-Methods"] = "OPTIONS,GET,POST,PUT,DELETE" @Controller(url=BASE_URL + "$", method="GET") def get_collection(self, request, version, resource_url): '''This method provides the route for accessing a resource collection. :doc:`/features/roa/rest_standard` for collections are enabled by this method. The typical response format is presented below: .. code-block:: javascript var response = {"items": [ // resources represented as json objects. ], "totalItems": 100} If a resource is not found or the resource version does not exist the following response is returned: .. code-block:: javascript {"error_code": 10000, "error_description": "Resource %s version %s does not exist.", "error_details": "http://rcosnita.github.io/fantastico/html/features/roa/errors/error_10000.html"} ''' if version != "latest": version = float(version) params = CollectionParams(request, RoaController.OFFSET_DEFAULT, RoaController.LIMIT_DEFAULT) resource = self._resources_registry.find_by_url(resource_url, version) if not resource: return self._handle_resource_notfound(version, resource_url) self._inject_security_context(request, resource.model) access_token = self.validate_security_context(request, "read") json_serializer = self._json_serializer_cls(resource) filter_expr = self._parse_filter(params.filter_expr, resource.model) if resource.user_dependent: if filter_expr: filter_expr = ModelFilterAnd( filter_expr, ModelFilter(resource.model.user_id, access_token.user_id, ModelFilter.EQ)) else: filter_expr = ModelFilter(resource.model.user_id, access_token.user_id, ModelFilter.EQ) sort_expr = self._parse_sort(params.order_expr, resource.model) model_facade = self._model_facade_cls( resource.model, self._get_current_connection(request)) models = model_facade.get_records_paged(start_record=params.offset, end_record=params.offset + params.limit, filter_expr=filter_expr, sort_expr=sort_expr) items = [ json_serializer.serialize(model, params.fields) for model in models ] if resource.validator: resource.validator().format_collection(items, request) models_count = model_facade.count_records(filter_expr=filter_expr) body = {"items": items, "totalItems": models_count} response = Response(text=json.dumps(body), content_type="application/json", status_code=200) self._add_cors_headers(response) return response @Controller(url=BASE_LATEST_URL + "$", method="GET") def get_collection_latest(self, request, resource_url): '''This method retrieves a resource collection using the latest version of the api.''' return self.get_collection(request, "latest", self._trim_resource_url(resource_url)) @Controller(url=[BASE_URL + "$", BASE_URL + "/(?P<resource_id>.*?)$"], method="OPTIONS") # pylint: disable=W0613 @CorsEnabled() def handle_resource_options(self, request, version, resource_url, **kwargs): '''This method enables support for http ajax CORS requests. This is mandatory if we want to host apis on different domains than project host.''' pass @Controller(url=[ BASE_LATEST_URL + "$", BASE_LATEST_URL + "/(?P<resource_id>.*?)$" ], method="OPTIONS") def handle_resource_options_latest(self, request, resource_url, **kwargs): '''This method handles OPTIONS http requests for ROA api latest versions.''' return self.handle_resource_options( request, "latest", self._trim_resource_url(resource_url), **kwargs) def _validate_resource(self, resource, request, request_body, existing_resource_id=None): '''This method is used to validate the resource. If the resource validation fails an error response is sent. Otherwise the newly validated model is returned.''' if not request_body: return self._handle_resource_nobody(resource.version, resource.url) model = self._json_serializer_cls(resource).deserialize( request_body.decode()) action = "create" if existing_resource_id: action = "update" self._inject_security_context(request, model) self.validate_security_context(request, action) if not resource.validator: return model try: resource.validator().validate(model, request, existing_resource_id) except FantasticoRoaError as ex: return self._handle_resource_invalid(resource.version, resource.url, ex) return model @Controller(url=BASE_URL + "/(?P<resource_id>.*?)$", method="GET") def get_item(self, request, version, resource_url, resource_id): '''This method provides the API for retrieving a single item from a collection. The item is uniquely identified by resource_id. Below you can find a success response example: .. code-block:: html GET - /api/1.0/simple-resources/1 HTTP/1.1 200 OK Content-Type: application/json Content-Length: ... { "id": 1, "name": "Test resource", "description": "Simple description" } Of course there are cases when exceptions might occur. Below, you can find a list of error response retrieved from get_item API: * **10000** - Whenever we try to retrieve a resource with unknown type. (Not registered to ROA). * **10030** - Whenever we try to retrieve a resource and an unexpected database exception occurs. * **10040** - Whenever we try to retrieve a resource which does not exist. ''' if version != "latest": version = float(version) fields = request.params.get("fields") resource = self._resources_registry.find_by_url(resource_url, version) if not resource: return self._handle_resource_notfound(version, resource_url) self._inject_security_context(request, resource.model) access_token = self.validate_security_context(request, "read") model_facade = self._model_facade_cls( resource.model, self._get_current_connection(request)) try: model = model_facade.find_by_pk( {model_facade.model_pk_cols[0]: resource_id}) if not self._is_model_owned_by(model, access_token, resource): model = None except FantasticoDbError as dbex: return self._handle_resource_dberror(version, resource_url, dbex) if not model: return self._handle_resource_item_notfound(version, resource_url, resource_id) json_serializer = self._json_serializer_cls(resource) resource_body = json_serializer.serialize(model, fields) if resource.validator and model: resource.validator().format_resource( DictionaryObject(resource_body, immutable=False), request) resource_body = json.dumps(resource_body) response = Response(body=resource_body.encode(), content_type="application/json", status_code=200) self._add_cors_headers(response) return response @Controller(url=BASE_LATEST_URL + "/(?P<resource_id>.*?)$", method="GET") def get_item_latest(self, request, resource_url, resource_id): '''This method provides the latest get_item route for ROA api.''' return self.get_item(request, "latest", self._trim_resource_url(resource_url), resource_id) @Controller(url=BASE_URL + "$", method="POST") def create_item(self, request, version, resource_url): '''This method provides the route for adding new resources into an existing collection. The API is json only and invoke the validator as described in ROA spec. Usually, when a resource is created successfully a similar answer is returned to the client: .. code-block:: html 201 Created Content-Type: application/json Content-Length: 0 Location: /api/2.0/app-settings/123 Below you can find all error response codes which might be returned when creating a new resource: * **10000** - Whenever we try to create a resource with unknown type. (Not registered to ROA). * **10010** - Whenever we try to create a resource which fails validation. * **10020** - Whenever we try to create a resource without passing a valid body. * **10030** - Whenever we try to create a resource and an unexpected database exception occurs. You can find more information about typical REST ROA APIs response on :doc:`/features/roa/rest_responses`.''' if version != "latest": version = float(version) resource = self._resources_registry.find_by_url(resource_url, version) if not resource: return self._handle_resource_notfound(version, resource_url) model = self._validate_resource(resource, request, request.body) access_token = request.context.security.access_token if isinstance(model, Response): return model try: if resource.user_dependent and access_token: model.user_id = access_token.user_id if resource.validator: resource.validator().on_pre_create(model, request) model_facade = self._model_facade_cls( resource.model, self._get_current_connection(request)) model_id = model_facade.create(model)[0] if resource.validator: resource.validator().on_post_create(model, request) except FantasticoDbError as dbex: return self._handle_resource_dberror(resource.version, resource.url, dbex) model_location = roa_helper.calculate_resource_url( self._roa_api, resource, version) model_location += "/%s" % model_id response = Response(status_code=201, content_type="application/json") self._add_cors_headers(response) response.headers["Location"] = model_location return response @Controller(url=BASE_LATEST_URL + "$", method="POST") def create_item_latest(self, request, resource_url): '''This method provides create item latest API version.''' return self.create_item(request, "latest", self._trim_resource_url(resource_url)) @Controller(url=BASE_URL + "/(?P<resource_id>.*?)$", method="PUT") def update_item(self, request, version, resource_url, resource_id): '''This method provides the route for updating existing resources from an existing collection. The API is json only and invokes the validator as described in ROA spec. Usually, when a resource is update successfully a similar answer is returned to the client: .. code-block:: html 204 No Content Content-Type: application/json Content-Length: 0 Below you can find all error response codes which might be returned when creating a new resource: * **10000** - Whenever we try to update a resource with unknown type. (Not registered to ROA). * **10010** - Whenever we try to update a resource which fails validation. * **10020** - Whenever we try to update a resource without passing a valid body. * **10030** - Whenever we try to update a resource and an unexpected database exception occurs. * **10040** - Whenever we try to update a resource which does not exist. You can find more information about typical REST ROA APIs response on :doc:`/features/roa/rest_responses`.''' if version != "latest": version = float(version) resource = self._resources_registry.find_by_url(resource_url, version) if not resource: return self._handle_resource_notfound(version, resource_url) model = self._validate_resource(resource, request, request.body, resource_id) if isinstance(model, Response): return model access_token = request.context.security.access_token model_facade = self._model_facade_cls( resource.model, self._get_current_connection(request)) pk_col = model_facade.model_pk_cols[0] try: existing_model = model_facade.find_by_pk({pk_col: resource_id}) if not existing_model or not self._is_model_owned_by( existing_model, access_token, resource): return self._handle_resource_item_notfound( version, resource_url, resource_id) setattr(model, pk_col.name, resource_id) if resource.validator: resource.validator().on_pre_update(model, request) model_facade.update(model) if resource.validator: resource.validator().on_post_update(model, request) except FantasticoDbError as dbex: return self._handle_resource_dberror(resource.version, resource.url, dbex) response = Response(content_type="application/json", status_code=204) self._add_cors_headers(response) return response @Controller(url=BASE_LATEST_URL + "/(?P<resource_id>.*?)$", method="PUT") def update_item_latest(self, request, resource_url, resource_id): '''This is the route handler for latest update existing item api.''' return self.update_item(request, "latest", self._trim_resource_url(resource_url), resource_id) @Controller(url=BASE_URL + "/(?P<resource_id>.*?)$", method="DELETE") def delete_item(self, request, version, resource_url, resource_id): '''This method provides the route for deleting existing resources from an existing collection. The API is json only. Usually, when a resource is deleted successfully a similar answer is returned to the client: .. code-block:: html 204 No Content Content-Type: application/json Content-Length: 0 Below you can find all error response codes which might be returned when creating a new resource: * **10000** - Whenever we try to delete a resource with unknown type. (Not registered to ROA). * **10030** - Whenever we try to delete a resource and an unexpected database exception occurs. * **10040** - Whenever we try to delete a resource which does not exist. You can find more information about typical REST ROA APIs response on :doc:`/features/roa/rest_responses`.''' if version != "latest": version = float(version) resource = self._resources_registry.find_by_url(resource_url, version) if not resource: return self._handle_resource_notfound(version, resource_url) self._inject_security_context(request, resource.model) access_token = self.validate_security_context(request, "delete") try: model_facade = self._model_facade_cls( resource.model, self._get_current_connection(request)) pk_col = model_facade.model_pk_cols[0] existing_model = model_facade.find_by_pk({pk_col: resource_id}) if not self._is_model_owned_by(existing_model, access_token, resource): existing_model = None if not existing_model: return self._handle_resource_item_notfound( version, resource_url, resource_id) if resource.validator: resource.validator().on_pre_delete(existing_model, request) model_facade.delete(existing_model) if resource.validator: resource.validator().on_post_delete(existing_model, request) except FantasticoDbError as dbex: return self._handle_resource_dberror(version, resource_url, dbex) response = Response(content_type="application/json", status_code=204) self._add_cors_headers(response) return response @Controller(url=BASE_LATEST_URL + "/(?P<resource_id>.*?)$", method="DELETE") def delete_item_latest(self, request, resource_url, resource_id): '''This method provides the functionality for delete item latest version api route.''' return self.delete_item(request, "latest", self._trim_resource_url(resource_url), resource_id) def validate_security_context(self, request, attr_scope): '''This method triggers security context validation and converts unexpected exceptions to OAuth2UnauthorizedError. If everything is fine this method return the access_token from security context.''' security_ctx = request.context.security if not attr_scope: return try: if not security_ctx.validate_context(attr_scope): raise OAuth2UnauthorizedError( "Security context insufficient scopes.") except OAuth2Error: raise except Exception as ex: raise OAuth2UnauthorizedError( "Security context validation failed: %s" % str(ex)) return security_ctx.access_token def _inject_security_context(self, request, resource_model): '''This method enrich the current security context with required scopes (if necessary).''' if not hasattr(resource_model, "get_required_scopes"): return required_scopes_obj = resource_model.get_required_scopes() required_scopes_obj.inject_scopes_in_security(request) def _is_model_owned_by(self, model, access_token, resource): '''This method is used to detect if a given model can be accessed using the access token. If the resource is not user dependent this method always returns true.''' if resource.user_dependent: if model.user_id == access_token.user_id: return True return False return True