def row(user, database, table, row, format=None): require(user, database, 'read', format) _table = _get_table(user, database, table, format) try: row = int(row) except ValueError: raise WebstoreException('Invalid row ID: %s' % row, format, state='error', code=400) if row == 0: raise WebstoreException('Starting at offset 1 to allow header row', format, state='error', code=400) params, select_args = _request_query(_table, request.args, format) select_args['limit'] = 1 select_args['offset'] = row - 1 try: statement = _table.table.select('', **select_args) log.debug("Read row: %s" % statement) results = _table.bind.execute(statement) except OperationalError, oe: raise WebstoreException('Invalid query: %s' % oe.message, format, state='error', code=400)
def sql(user, database, format=None): """ Execute an SQL statement on the database. """ # TODO: do we really, really need this? if request.content_type == 'text/sql': query = request.data params = None attaches = [] elif request.content_type.lower() == 'application/json': query = request.json.get('query', '') params = request.json.get('params') attaches = request.json.get('attach', []) else: raise WebstoreException('Only text/sql, application/json is supported', format, state='error', code=400) try: if has(user, database, 'delete'): db = db_factory.create(user, database) else: require(user, database, 'read', format) db = db_factory.create_readonly(user, database) except NamingException, ne: raise WebstoreException('Invalid DB name: %s' % ne.field, format, state='error', code=400)
def _get_table(user, database, table, format): """ Locate a named table or raise a 404. """ try: db = db_factory.create(user, database) except NamingException, ne: raise WebstoreException('Invalid DB name: %s' % ne.field, format, state='error', code=400)
def upsert(user, database, table, format=None): require(user, database, 'write', format) try: db = db_factory.create(user, database) except NamingException, ne: raise WebstoreException('Invalid DB name: %s' % ne.field, format, state='error', code=400)
def never_login(request): if 'Authorization' in request.headers: raise WebstoreException('Invalid username or password!', None, state='error', code=401) return None
def check_ckan_login(request): """ Connect to a specified CKAN database via SQLAlchemy and try to find the user that is authenticating. """ db_uri = app.config.get('CKAN_DB_URI') if db_uri is None: log.warn("No CKAN_DB_URI given, cannot authenticate!") return False if 'Authorization' in request.headers: authorization = request.headers.get('Authorization') authorization = authorization.split(' ', 1)[-1] user, password = authorization.decode('base64').split(':', 1) engine = create_engine(db_uri, poolclass=NullPool) meta = MetaData() meta.bind = engine table = Table('user', meta, autoload=True) results = engine.execute(table.select(table.c.name == user)) # TODO: check for multiple matches, never trust ckan. record = results.first() if record is not None and check_hashed_password( password, record['password']): return user raise WebstoreException('Invalid username or password!', None, state='error', code=401) return None
def delete(user, database, table, format=None): require(user, database, 'delete', format) _table = _get_table(user, database, table, format) _table.drop() log.debug("Dropping: %s" % _table.table.name) _table.commit() raise WebstoreException('Table dropped: %s' % table, format, state='success', code=410)
def index(user, database, format=None): """ Give a list of all tables in the database. """ require(user, database, 'read', format) try: db = db_factory.create(user, database) except NamingException, ne: raise WebstoreException('Invalid name: %s' % ne.field, format, state='error', code=400)
def read(user, database, table, format=None): require(user, database, 'read', format) _table = _get_table(user, database, table, format) params, select_args = _request_query(_table, request.args, format) try: clause = _table.args_to_clause(params) except KeyError, ke: raise WebstoreException('Invalid filter: %s' % ke, format, state='error', code=400)
def require(user, database, action, format): """ Require the current user to have the right to execute `action` on `database` of `user`. If this right is not given, raise an exception. """ if not has(user, database, action): raise WebstoreException('No permission to %s %s' % (action, database), format, state='error', code=403)
def distinct(user, database, table, column, format=None): require(user, database, 'read', format) _table = _get_table(user, database, table, format) if not column in _table.table.columns: raise WebstoreException('No such column: %s' % column, format, state='error', code=404) params, select_args = _request_query(_table, request.args, format) select_args['distinct'] = True if not len(select_args['order_by']): select_args['order_by'].append(desc('_count')) try: col = _table.table.c[column] statement = select([col, func.count(col).label('_count')], group_by=[col], **select_args) log.debug("Distinct: %s" % statement) results = _table.bind.execute(statement) except OperationalError, oe: raise WebstoreException('Invalid query: %s' % oe.message, format, state='error', code=400)
def sw_auth(request): """ Authenticate an incoming request. """ current_app.sw_scrapername = request.headers.get('X-Scrapername') if current_app.sw_scrapername is not None: answer = sha256(current_app.sw_scrapername + \ current_app.config['SW_SECRET']).hexdigest() candidate = request.headers.get('X-Scraper-Verified') if candidate != answer: raise WebstoreException('Invalid ScraperWiki verification!', None, state='error', code=401)
def databases(user, format=None): """ Give a list of all databases owned by the user. """ #require(user, database, 'read', format) try: databases = [] for database in db_factory.databases_by_user(user): url = url_for('webstore.index', user=user, database=database) databases.append({'name': database, 'url': url}) return render_table(request, databases, ['name', 'url'], format) except NamingException, ne: raise WebstoreException('Invalid name: %s' % ne.field, format, state='error', code=400)
def databases(user, format=None): """ Give a list of all databases owned by the user. """ #require(user, database, 'read', format) try: databases = [] # TODO: this is a hack, find a nicer way to do this # # we want to allow user names to contain a '.', eg: 'thedatahub.org' # this breaks the routing for this function # # so if format exists, check that it should not be part of the user name if format: try: user_databases = db_factory.databases_by_user(user + '.' + format) user = user + '.' + format except UserNotFound: user_databases = db_factory.databases_by_user(user) else: user_databases = db_factory.databases_by_user(user) for database in user_databases: url = url_for('webstore.index', user=user, database=database) databases.append({'name': database, 'url': url}) return render_table(request, databases, ['name', 'url'], format) except UserNotFound: raise WebstoreException('User not found', format, state='error', code=404) except NamingException, ne: raise WebstoreException('Invalid name: %s' % ne.field, format, state='error', code=400)
def _request_query(_table, _params, format): """ From a set of query parameters, apply those that affect a query result set, e.g. sorting, limiting and offsets. Returns a tuple of the remaining query parameters and a curried call to the database. """ params = _params.copy() if '_callback' in params: params.pop('_callback') try: limit = int(params.pop('_limit', None)) \ if '_limit' in params else None offset = int(params.pop('_offset', None)) \ if '_offset' in params else None except ValueError, ve: raise WebstoreException('Invalid value: %s' % ve, format, state='error', code=400)
def check_ckan_login(request): """ Connect to a specified CKAN database via SQLAlchemy and try to find the user that is authenticating. """ db_uri = app.config.get('CKAN_DB_URI') if db_uri is None: log.warn("No CKAN_DB_URI given, cannot authenticate!") return False if 'Authorization' in request.headers: apikey = request.headers.get('Authorization') engine = create_engine(db_uri, poolclass=NullPool) meta = MetaData() meta.bind = engine table = Table('user', meta, autoload=True) results = engine.execute(table.select(table.c.apikey == apikey)) # TODO: check for multiple matches, never trust ckan. record = results.first() if record is not None: return record['name'] raise WebstoreException('Invalid apikey!', None, state='error', code=401) return None
@store.before_app_request def jsonp_callback_register(): # This is a slight hack to not make JSON-P callback names # end up in the query string, we'll keep it around on the # request global and then read it in the table generator. g.callback = request.args.get('_callback') def _get_table(user, database, table, format): """ Locate a named table or raise a 404. """ try: db = db_factory.create(user, database) except NamingException, ne: raise WebstoreException('Invalid DB name: %s' % ne.field, format, state='error', code=400) if not table in db: raise WebstoreException('No such table: %s' % table, format, state='error', code=404) return db[table] def _request_query(_table, _params, format): """ From a set of query parameters, apply those that affect a query result set, e.g. sorting, limiting and offsets. Returns a tuple of the remaining query parameters and a curried call to the database. """ params = _params.copy() if '_callback' in params: params.pop('_callback') try: