def test02_logout_service_description(self): auth = IIIFAuthBasic() auth.logout_uri = 'xyz' lsd = auth.logout_service_description() self.assertEqual( lsd['profile'], 'http://iiif.io/api/auth/0/logout' ) self.assertEqual( lsd['@id'], 'xyz' ) self.assertEqual( lsd['label'], 'Logout from image server' )
def test05_login_handler(self): """Test login_handler.""" with dummy_app.test_request_context('/a_request'): auth = IIIFAuthBasic() response = auth.login_handler() self.assertEqual(response.status_code, 401) self.assertEqual(response.headers['Content-type'], 'text/html') html = response.get_data().decode('utf-8') # data is bytes in python3 self.assertEqual(html, '') # add good login params and check OK, window close h = Headers() h.add('Authorization', b'Basic ' + base64.b64encode(b'userpass:userpass')) with dummy_app.test_request_context('/a_request', headers=h): response = auth.login_handler() self.assertEqual(response.status_code, 200) html = response.get_data().decode('utf-8') self.assertTrue( re.search( r'<script>window.close\(\);</script>', html)) set_cookie = response.headers['Set-Cookie'] self.assertTrue( re.search( auth.auth_cookie_name + '=valid-http-basic-login', set_cookie)) # add bad login params and check fail h = Headers() h.add('Authorization', b'Basic ' + base64.b64encode(b'userpass:bad-pass')) with dummy_app.test_request_context('/a_request', headers=h): response = auth.login_handler() self.assertEqual(response.status_code, 401)
def test06_logout_handler(self): with dummy_app.test_request_context('/a_request'): auth = IIIFAuthBasic() response = auth.logout_handler() self.assertEqual( response.status_code, 200 ) self.assertEqual( response.headers['Content-type'], 'text/html' ) html = response.get_data() self.assertTrue( re.search(r'<script>window.close\(\);</script>',html) )
def test02_logout_service_description(self): """Test logout_service_description.""" auth = IIIFAuthBasic() auth.logout_uri = 'xyz' lsd = auth.logout_service_description() self.assertEqual(lsd['profile'], 'http://iiif.io/api/auth/0/logout') self.assertEqual(lsd['@id'], 'xyz') self.assertEqual(lsd['label'], 'Logout from image server')
def test06_logout_handler(self): """Test logout_handler.""" with dummy_app.test_request_context('/a_request'): auth = IIIFAuthBasic() response = auth.logout_handler() self.assertEqual(response.status_code, 200) self.assertEqual(response.headers['Content-type'], 'text/html') html = response.get_data().decode( 'utf-8') # get_data is bytes in python3 self.assertTrue( re.search(r'<script>window.close\(\);</script>', html))
def test07_access_token_handler(self): """Test access_token_handler.""" with dummy_app.test_request_context('/a_request'): auth = IIIFAuthBasic() response = auth.access_token_handler() self.assertEqual(response.status_code, 200) self.assertEqual( response.headers['Content-type'], 'application/json') # get_data is bytes in python3 j = json.loads(response.get_data().decode('utf-8')) self.assertEqual( j['error_description'], "No login details received") self.assertEqual(j['error'], "client_unauthorized") # add Authorization header, check we get token h = Headers() h.add('Authorization', b'Basic ' + base64.b64encode(b'userpass:userpass')) with dummy_app.test_request_context('/a_request', headers=h): auth = IIIFAuthBasic() response = auth.access_token_handler() self.assertEqual(response.status_code, 200) self.assertEqual( response.headers['Content-type'], 'application/json') j = json.loads(response.get_data().decode('utf-8')) self.assertEqual(j['access_token'], "secret_token_here") # FIXME self.assertEqual(j['token_type'], "Bearer") self.assertEqual(j['expires_in'], 3600) # add callback but no Authorization header with dummy_app.test_request_context('/a_request?callback=CB'): auth = IIIFAuthBasic() response = auth.access_token_handler() self.assertEqual(response.status_code, 200) self.assertEqual( response.headers['Content-type'], 'application/javascript') # strip JavaScript wrapper and then check JSON js = response.get_data().decode('utf-8') self.assertTrue(re.match('CB\(.*\);', js)) j = json.loads(js.lstrip('CB(').rstrip(');')) self.assertEqual( j['error_description'], "No login details received") self.assertEqual(j['error'], "client_unauthorized")
def test07_access_token_handler(self): with dummy_app.test_request_context('/a_request'): auth = IIIFAuthBasic() response = auth.access_token_handler() self.assertEqual( response.status_code, 200 ) self.assertEqual( response.headers['Content-type'], 'application/json' ) j = json.loads(response.get_data()) self.assertEqual( j['error_description'], "No login details received" ) self.assertEqual( j['error'], "client_unauthorized" ) # add Authorization header, check we get token h = Headers() h.add('Authorization', 'Basic ' + base64.b64encode('userpass:userpass')) with dummy_app.test_request_context('/a_request', headers=h): auth = IIIFAuthBasic() response = auth.access_token_handler() self.assertEqual( response.status_code, 200 ) self.assertEqual( response.headers['Content-type'], 'application/json' ) j = json.loads(response.get_data()) self.assertEqual( j['access_token'], "secret_token_here" ) #FIXME self.assertEqual( j['token_type'], "Bearer" ) self.assertEqual( j['expires_in'], 3600 ) # add callback but no Authorization header with dummy_app.test_request_context('/a_request?callback=CB'): auth = IIIFAuthBasic() response = auth.access_token_handler() self.assertEqual( response.status_code, 200 ) self.assertEqual( response.headers['Content-type'], 'application/javascript' ) # strip JavaScript wrapper and then check JSON js = response.get_data() self.assertTrue( re.match('CB\(.*\);',js) ) j = json.loads(js.lstrip('CB(').rstrip(');')) self.assertEqual( j['error_description'], "No login details received" ) self.assertEqual( j['error'], "client_unauthorized" )
def test26_IIIFHandler_image_information_response(self): """Test IIIFHandler.image_information_response().""" c = Config() c.api_version = '2.1' c.klass_name = 'dummy' c.image_dir = os.path.join(os.path.dirname(__file__), '../testimages') c.tile_height = 512 c.tile_width = 512 c.scale_factors = [1, 2] c.host = 'example.org' c.port = 80 i = IIIFHandler(prefix='p', identifier='starfish', config=c, klass=IIIFManipulator, auth=IIIFAuthBasic()) environ = WSGI_ENVIRON() with self.test_app.request_context(environ): resp = i.image_information_response() jsonb = resp.response[0] self.assertIn(b'default', jsonb) self.assertNotIn(b'native', jsonb) self.assertIn(b'starfish', jsonb) self.assertIn(b'scaleFactors', jsonb) self.assertIn(b'login', jsonb) # v1.1, auto scale factors c.api_version = '1.1' c.scale_factors = ['auto'] i = IIIFHandler(prefix='p', identifier='starfish', config=c, klass=IIIFManipulator, auth=None) with self.test_app.request_context(environ): resp = i.image_information_response() jsonb = resp.response[0] self.assertNotIn(b'default', jsonb) self.assertIn(b'native', jsonb) self.assertIn(b'starfish', jsonb) self.assertNotIn(b'scaleFactors', jsonb) # degraded c.api_version = '2.1' i = IIIFHandler(prefix='p', identifier='starfish-deg', config=c, klass=IIIFManipulator, auth=None) with self.test_app.request_context(environ): resp = i.image_information_response() jsonb = resp.response[0] self.assertIn(b'starfish-deg', jsonb)
def test21_IIIFHandler_init(self): """Test IIIFHandler class init.""" # No auth c = Config() c.api_version = '2.1' i = IIIFHandler(prefix='/p', identifier='i', config=c, klass=IIIFManipulator, auth=None) self.assertTrue(i.manipulator.api_version, '2.1') # Basic auth a = IIIFAuthBasic() c.host = 'example.org' c.port = 80 c.prefix = '/p' i = IIIFHandler(prefix='/p', identifier='i', config=c, klass=IIIFManipulator, auth=a) self.assertTrue(i.manipulator.api_version, '2.1')
def add_handler(app, config, prefixes): """Add a single handler to the app. Adds handlers to app, with config from config. Add prefix to list in prefixes. """ wsgi_prefix = make_prefix(config.api_version, config.klass_name, config.auth_type) prefix = wsgi_prefix if (config.container_prefix): prefix = os.path.join(config.container_prefix, wsgi_prefix) prefixes.append(prefix) auth = None if (config.auth_type is None or config.auth_type == 'none'): pass elif (config.auth_type == 'gauth'): from iiif.auth_google import IIIFAuthGoogle auth = IIIFAuthGoogle( client_secret_file=config.gauth_client_secret_file) elif (config.auth_type == 'basic'): from iiif.auth_basic import IIIFAuthBasic auth = IIIFAuthBasic() elif (config.auth_type == 'clickthrough'): from iiif.auth_clickthrough import IIIFAuthClickthrough auth = IIIFAuthClickthrough() elif (config.auth_type == 'kiosk'): from iiif.auth_kiosk import IIIFAuthKiosk auth = IIIFAuthKiosk() else: print("Unknown auth type %s, ignoring" % (config.auth_type)) return if (auth is not None): auth.access_cookie_lifetime = opt.access_cookie_lifetime auth.access_token_lifetime = opt.access_token_lifetime klass = None if (config.klass_name == 'pil'): from iiif.manipulator_pil import IIIFManipulatorPIL klass = IIIFManipulatorPIL elif (config.klass_name == 'netpbm'): from iiif.manipulator_netpbm import IIIFManipulatorNetpbm klass = IIIFManipulatorNetpbm elif (config.klass_name == 'dummy'): from iiif.manipulator import IIIFManipulator klass = IIIFManipulator elif (config.klass_name == 'gen'): from iiif.manipulator_gen import IIIFManipulatorGen klass = IIIFManipulatorGen else: print("Unknown manipulator type %s, ignoring" % (config.klass_name)) return print("Installing %s IIIFManipulator at /%s/ v%s %s" % (config.klass_name, prefix, config.api_version, config.auth_type)) params = dict(config=config, klass=klass, auth=auth, prefix=prefix) app.add_url_rule('/' + wsgi_prefix, 'prefix_index_page', prefix_index_page, defaults={ 'config': config, 'prefix': prefix }) app.add_url_rule('/' + wsgi_prefix + '/<string(minlength=1):identifier>/info.json', 'options_handler', options_handler, methods=['OPTIONS']) app.add_url_rule('/' + wsgi_prefix + '/<string(minlength=1):identifier>/info.json', 'iiif_info_handler', iiif_info_handler, methods=['GET'], defaults=params) if (config.include_osd): app.add_url_rule('/' + wsgi_prefix + '/<string(minlength=1):identifier>/osd.html', 'osd_page_handler', osd_page_handler, methods=['GET'], defaults=params) app.add_url_rule('/' + wsgi_prefix + '/<string(minlength=1):identifier>/<path:path>', 'iiif_image_handler', iiif_image_handler, methods=['GET'], defaults=params) if (auth): setup_auth_paths(app, auth, wsgi_prefix, params) # redirects to info.json must come after auth app.add_url_rule('/' + wsgi_prefix + '/<string(minlength=1):identifier>', 'iiif_info_handler', redirect_to='/' + prefix + '/<identifier>/info.json') app.add_url_rule('/' + wsgi_prefix + '/<string(minlength=1):identifier>/', 'iiif_info_handler', redirect_to='/' + prefix + '/<identifier>/info.json')
def add_handler(app, config): """Add a single handler to the app. Adds one IIIF Image API handler to app, with config from config. Arguments: app - Flask app config - Configuration object in which: config.prefix - String path prefix for this handler config.client_prefix - String path prefix seen by client (which may be different because of reverse proxy or such config.klass_name - Manipulator class, e.g. 'pil' config.api_version - e.g. '2.1' config.include_osd - True or False to include OSD config.gauth_client_secret_file - filename if auth_type='gauth' config.access_cookie_lifetime - number of seconds config.access_token_lifetime - number of seconds config.auth_type - Auth type string or 'none' Returns True on success, nothing otherwise. """ auth = None if (config.auth_type is None or config.auth_type == 'none'): pass elif (config.auth_type == 'gauth'): from iiif.auth_google import IIIFAuthGoogle auth = IIIFAuthGoogle( client_secret_file=config.gauth_client_secret_file) elif (config.auth_type == 'basic'): from iiif.auth_basic import IIIFAuthBasic auth = IIIFAuthBasic() elif (config.auth_type == 'clickthrough'): from iiif.auth_clickthrough import IIIFAuthClickthrough auth = IIIFAuthClickthrough() elif (config.auth_type == 'kiosk'): from iiif.auth_kiosk import IIIFAuthKiosk auth = IIIFAuthKiosk() elif (config.auth_type == 'external'): from iiif.auth_external import IIIFAuthExternal auth = IIIFAuthExternal() else: logging.error("Unknown auth type %s, ignoring" % (config.auth_type)) return if (auth is not None): auth.access_cookie_lifetime = config.access_cookie_lifetime auth.access_token_lifetime = config.access_token_lifetime klass = None if (config.klass_name == 'pil'): from iiif.manipulator_pil import IIIFManipulatorPIL klass = IIIFManipulatorPIL elif (config.klass_name == 'netpbm'): from iiif.manipulator_netpbm import IIIFManipulatorNetpbm klass = IIIFManipulatorNetpbm elif (config.klass_name == 'dummy'): from iiif.manipulator import IIIFManipulator klass = IIIFManipulator elif (config.klass_name == 'gen'): from iiif.manipulator_gen import IIIFManipulatorGen klass = IIIFManipulatorGen else: logging.error("Unknown manipulator type %s, ignoring" % (config.klass_name)) return base = urljoin('/', config.prefix + '/') # ensure has trailing slash client_base = urljoin('/', config.client_prefix + '/') # ensure has trailing slash logging.warning( "Installing %s IIIFManipulator at %s v%s %s" % (config.klass_name, base, config.api_version, config.auth_type)) params = dict(config=config, klass=klass, auth=auth, prefix=config.client_prefix) app.add_url_rule(base.rstrip('/'), 'prefix_index_page', prefix_index_page, defaults={'config': config}) app.add_url_rule(base, 'prefix_index_page', prefix_index_page, defaults={'config': config}) app.add_url_rule(base + '<string(minlength=1):identifier>/info.json', 'options_handler', options_handler, methods=['OPTIONS']) app.add_url_rule(base + '<string(minlength=1):identifier>/info.json', 'iiif_info_handler', iiif_info_handler, methods=['GET'], defaults=params) if (config.include_osd): app.add_url_rule(base + '<string(minlength=1):identifier>/osd.html', 'osd_page_handler', osd_page_handler, methods=['GET'], defaults=params) app.add_url_rule(base + '<string(minlength=1):identifier>/<path:path>', 'iiif_image_handler', iiif_image_handler, methods=['GET'], defaults=params) if (auth): setup_auth_paths(app, auth, config.client_prefix, params) # redirects to info.json must come after auth app.add_url_rule(base + '<string(minlength=1):identifier>', 'iiif_info_handler', redirect_to=client_base + '<identifier>/info.json') app.add_url_rule(base + '<string(minlength=1):identifier>/', 'iiif_info_handler', redirect_to=client_base + '<identifier>/info.json') return True
def test04_image_authn(self): with dummy_app.test_request_context('/a_request'): auth = IIIFAuthBasic() ia = auth.image_authn() self.assertEqual( ia, '' )
def test03_info_authn(self): with dummy_app.test_request_context('/a_request'): auth = IIIFAuthBasic() ia = auth.info_authn() self.assertEqual( ia, False )
def test04_image_authn(self): """Test image_authn.""" with dummy_app.test_request_context('/a_request'): auth = IIIFAuthBasic() ia = auth.image_authn() self.assertEqual(ia, '')
def test03_info_authn(self): """Test info_authn.""" with dummy_app.test_request_context('/a_request'): auth = IIIFAuthBasic() ia = auth.info_authn() self.assertEqual(ia, False)
def test01_init(self): """Test inialization.""" auth = IIIFAuthBasic() self.assertTrue(re.match(r'\d+_', auth.cookie_prefix)) auth = IIIFAuthBasic(cookie_prefix='abc') self.assertEqual(auth.cookie_prefix, 'abc')