mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Access control for rendered books
This commit is contained in:
parent
85455cf5ae
commit
fc18242da3
@ -43,8 +43,8 @@ def books_cache_dir():
|
|||||||
return base
|
return base
|
||||||
|
|
||||||
|
|
||||||
def book_hash(library_uuid, book_id, fmt, fmt_metadata):
|
def book_hash(library_uuid, book_id, fmt, size, mtime):
|
||||||
raw = dumps((library_uuid, book_id, fmt.upper(), fmt_metadata['size'], fmt_metadata['mtime']), RENDER_VERSION)
|
raw = dumps((library_uuid, book_id, fmt.upper(), size, mtime), RENDER_VERSION)
|
||||||
return sha1(raw).hexdigest().decode('ascii')
|
return sha1(raw).hexdigest().decode('ascii')
|
||||||
|
|
||||||
staging_cleaned = False
|
staging_cleaned = False
|
||||||
@ -58,7 +58,7 @@ def safe_remove(x, is_file=None):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def queue_job(ctx, copy_format_to, bhash, fmt, book_id):
|
def queue_job(ctx, copy_format_to, bhash, fmt, book_id, size, mtime):
|
||||||
global staging_cleaned
|
global staging_cleaned
|
||||||
tdir = os.path.join(books_cache_dir(), 's')
|
tdir = os.path.join(books_cache_dir(), 's')
|
||||||
if not staging_cleaned:
|
if not staging_cleaned:
|
||||||
@ -69,7 +69,7 @@ def queue_job(ctx, copy_format_to, bhash, fmt, book_id):
|
|||||||
with os.fdopen(fd, 'wb') as f:
|
with os.fdopen(fd, 'wb') as f:
|
||||||
copy_format_to(f)
|
copy_format_to(f)
|
||||||
tdir = tempfile.mkdtemp('', '', tdir)
|
tdir = tempfile.mkdtemp('', '', tdir)
|
||||||
job_id = ctx.start_job('Render book %s (%s)' % (book_id, fmt), 'calibre.srv.render_book', 'render', args=(pathtoebook, tdir, bhash),
|
job_id = ctx.start_job('Render book %s (%s)' % (book_id, fmt), 'calibre.srv.render_book', 'render', args=(pathtoebook, tdir, (size, mtime)),
|
||||||
job_done_callback=job_done, job_data=(bhash, pathtoebook, tdir))
|
job_done_callback=job_done, job_data=(bhash, pathtoebook, tdir))
|
||||||
queued_jobs[bhash] = job_id
|
queued_jobs[bhash] = job_id
|
||||||
return job_id
|
return job_id
|
||||||
@ -94,32 +94,35 @@ def clean_final(interval=24 * 60 * 60):
|
|||||||
|
|
||||||
def job_done(job):
|
def job_done(job):
|
||||||
with cache_lock:
|
with cache_lock:
|
||||||
book_hash, pathtoebook, tdir = job.data
|
bhash, pathtoebook, tdir = job.data
|
||||||
queued_jobs.pop(book_hash, None)
|
queued_jobs.pop(bhash, None)
|
||||||
safe_remove(pathtoebook)
|
safe_remove(pathtoebook)
|
||||||
if job.failed:
|
if job.failed:
|
||||||
failed_jobs[book_hash] = (job.was_aborted, job.traceback)
|
failed_jobs[bhash] = (job.was_aborted, job.traceback)
|
||||||
safe_remove(tdir, False)
|
safe_remove(tdir, False)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
clean_final()
|
clean_final()
|
||||||
dest = os.path.join(books_cache_dir(), 'f', book_hash)
|
dest = os.path.join(books_cache_dir(), 'f', bhash)
|
||||||
safe_remove(dest, False)
|
safe_remove(dest, False)
|
||||||
os.rename(tdir, dest)
|
os.rename(tdir, dest)
|
||||||
except Exception:
|
except Exception:
|
||||||
import traceback
|
import traceback
|
||||||
failed_jobs[book_hash] = (False, traceback.format_exc())
|
failed_jobs[bhash] = (False, traceback.format_exc())
|
||||||
|
|
||||||
@endpoint('/book-manifest/{book_id}/{fmt}', postprocess=json, types={'book_id':int})
|
@endpoint('/book-manifest/{book_id}/{fmt}', postprocess=json, types={'book_id':int})
|
||||||
def book_manifest(ctx, rd, book_id, fmt):
|
def book_manifest(ctx, rd, book_id, fmt):
|
||||||
db, library_id = get_library_data(ctx, rd)[:2]
|
db, library_id = get_library_data(ctx, rd)[:2]
|
||||||
if plugin_for_input_format(fmt) is None:
|
if plugin_for_input_format(fmt) is None:
|
||||||
raise HTTPNotFound('The format %s cannot be viewed' % fmt.upper())
|
raise HTTPNotFound('The format %s cannot be viewed' % fmt.upper())
|
||||||
|
if book_id not in ctx.allowed_book_ids(rd, db):
|
||||||
|
raise HTTPNotFound('No book with id: %s in library: %s' % (book_id, library_id))
|
||||||
with db.safe_read_lock:
|
with db.safe_read_lock:
|
||||||
fm = db.format_metadata(book_id, fmt)
|
fm = db.format_metadata(book_id, fmt)
|
||||||
if not fm:
|
if not fm:
|
||||||
raise HTTPNotFound('No %s format for the book %s in the library: %s' % (fm, book_id, library_id))
|
raise HTTPNotFound('No %s format for the book %s in the library: %s' % (fm, book_id, library_id))
|
||||||
bhash = book_hash(db.library_id, book_id, fmt, fm)
|
size, mtime = map(int, (fm['size'], time.mktime(fm['mtime'].utctimetuple())*10))
|
||||||
|
bhash = book_hash(db.library_id, book_id, fmt, size, mtime)
|
||||||
with cache_lock:
|
with cache_lock:
|
||||||
mpath = abspath(os.path.join(books_cache_dir(), 'f', bhash, 'calibre-book-manifest.json'))
|
mpath = abspath(os.path.join(books_cache_dir(), 'f', bhash, 'calibre-book-manifest.json'))
|
||||||
try:
|
try:
|
||||||
@ -133,19 +136,23 @@ def book_manifest(ctx, rd, book_id, fmt):
|
|||||||
return {'aborted':x[0], 'traceback':x[1], 'job_status':'finished'}
|
return {'aborted':x[0], 'traceback':x[1], 'job_status':'finished'}
|
||||||
job_id = queued_jobs.get(bhash)
|
job_id = queued_jobs.get(bhash)
|
||||||
if job_id is None:
|
if job_id is None:
|
||||||
job_id = queue_job(ctx, partial(db.copy_format_to, book_id, fmt), bhash, fmt, book_id)
|
job_id = queue_job(ctx, partial(db.copy_format_to, book_id, fmt), bhash, fmt, book_id, size, mtime)
|
||||||
status, result, tb, aborted = ctx.job_status(job_id)
|
status, result, tb, aborted = ctx.job_status(job_id)
|
||||||
return {'aborted': aborted, 'traceback':tb, 'job_status':status, 'job_id':job_id}
|
return {'aborted': aborted, 'traceback':tb, 'job_status':status, 'job_id':job_id}
|
||||||
|
|
||||||
@endpoint('/book-file/{book_hash}/{name}')
|
@endpoint('/book-file/{book_id}/{fmt}/{size}/{mtime}/{+name}', types={'book_id':int, 'size':int, 'mtime':int})
|
||||||
def book_file(ctx, rd, book_hash, name):
|
def book_file(ctx, rd, book_id, fmt, size, mtime, name):
|
||||||
base = abspath(os.path.join(books_cache_dir, 'f'))
|
db, library_id = get_library_data(ctx, rd)[:2]
|
||||||
mpath = abspath(os.path.join(book_hash, name))
|
if book_id not in ctx.allowed_book_ids(rd, db):
|
||||||
|
raise HTTPNotFound('No book with id: %s in library: %s' % (book_id, library_id))
|
||||||
|
bhash = book_hash(db.library_id, book_id, fmt, size, mtime)
|
||||||
|
base = abspath(os.path.join(books_cache_dir(), 'f'))
|
||||||
|
mpath = abspath(os.path.join(base, bhash, name))
|
||||||
if not mpath.startswith(base):
|
if not mpath.startswith(base):
|
||||||
raise HTTPNotFound('No book file with hash: %s and name: %s' % (book_hash, name))
|
raise HTTPNotFound('No book file with hash: %s and name: %s' % (bhash, name))
|
||||||
try:
|
try:
|
||||||
return rd.filesystem_file_with_custom_etag(lopen(mpath, 'rb'), book_hash, name)
|
return rd.filesystem_file_with_custom_etag(lopen(mpath, 'rb'), bhash, name)
|
||||||
except EnvironmentError as e:
|
except EnvironmentError as e:
|
||||||
if e.errno != errno.ENOENT:
|
if e.errno != errno.ENOENT:
|
||||||
raise
|
raise
|
||||||
raise HTTPNotFound('No book file with hash: %s and name: %s' % (book_hash, name))
|
raise HTTPNotFound('No book file with hash: %s and name: %s' % (bhash, name))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user