def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) title_entry = find_entry(output_objects, 'title') title_entry['text'] = 'Delete frozen archive' defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) if not correct_handler('POST'): output_objects.append( {'object_type': 'error_text', 'text' : 'Only accepting POST requests to prevent unintended updates'}) return (output_objects, returnvalues.CLIENT_ERROR) flavor = accepted['flavor'][-1] if not flavor in freeze_flavors.keys(): output_objects.append({'object_type': 'error_text', 'text': 'Invalid freeze flavor: %s' % flavor}) return (output_objects, returnvalues.CLIENT_ERROR) title = freeze_flavors[flavor]['deletefreeze_title'] output_objects.append({'object_type': 'header', 'text': title}) title_entry = find_entry(output_objects, 'title') title_entry['text'] = title if not configuration.site_enable_freeze: output_objects.append({'object_type': 'text', 'text': '''Freezing archives is disabled on this site. Please contact the Grid admins %s if you think it should be enabled. ''' % configuration.admin_email}) return (output_objects, returnvalues.OK) freeze_id = accepted['freeze_id'][-1] # NB: the restrictions on freeze_id prevents illegal directory traversal if not is_frozen_archive(freeze_id, configuration): logger.error("%s: invalid freeze '%s': %s" % (op_name, client_id, freeze_id)) output_objects.append({'object_type': 'error_text', 'text': "No such frozen archive: '%s'" % freeze_id}) return (output_objects, returnvalues.CLIENT_ERROR) (load_status, freeze_dict) = get_frozen_archive(freeze_id, configuration) if not load_status: logger.error("%s: load failed for '%s': %s" % \ (op_name, freeze_id, freeze_dict)) output_objects.append( {'object_type': 'error_text', 'text': 'Could not read frozen archive details for %s' % freeze_id}) return (output_objects, returnvalues.SYSTEM_ERROR) # Prevent easy delete if the frozen archive if configuration forbids it if configuration.site_permanent_freeze: output_objects.append( {'object_type': 'error_text', 'text': "Can't delete frozen archive '%s' yourself due to site policy" % freeze_id}) return (output_objects, returnvalues.CLIENT_ERROR) # Make sure the frozen archive belongs to the user trying to delete it if client_id != freeze_dict['CREATOR']: logger.error("%s: illegal access attempt for '%s': %s" % \ (op_name, freeze_id, client_id)) output_objects.append({'object_type': 'error_text', 'text': \ 'You are not the owner of frozen archive "%s"' % freeze_id}) return (output_objects, returnvalues.CLIENT_ERROR) if freeze_dict.get('FLAVOR','freeze') != flavor: logger.error("%s: flavor mismatch for '%s': %s vs %s" % \ (op_name, freeze_id, flavor, freeze_dict)) output_objects.append({'object_type': 'error_text', 'text' : 'No such %s archive "%s"' % (flavor, freeze_id)}) return (output_objects, returnvalues.CLIENT_ERROR) # Delete the frozen archive (status, msg) = delete_frozen_archive(freeze_id, configuration) # If something goes wrong when trying to delete frozen archive # freeze_id, an error is displayed. if not status: logger.error("%s: failed for '%s': %s" % (op_name, freeze_id, msg)) output_objects.append({'object_type': 'error_text', 'text' : 'Could not remove %s frozen archive: %s' % (freeze_id, msg)}) return (output_objects, returnvalues.SYSTEM_ERROR) # If deletion of frozen archive freeze_id is successful, we just # return OK else: logger.info("%s: successful for '%s': %s" % (op_name, freeze_id, client_id)) output_objects.append( {'object_type': 'text', 'text' : 'Successfully deleted frozen archive: "%s"' % freeze_id}) output_objects.append({'object_type': 'link', 'destination': 'freezedb.py', 'class': 'infolink', 'title': 'Show frozen archives', 'text': 'Show frozen archives'}) return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) defaults = signature()[1] title_entry = find_entry(output_objects, 'title') title_entry['text'] = "Show freeze" (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) freeze_id = accepted['freeze_id'][-1] flavor = accepted['flavor'][-1] checksum_list = [i for i in accepted['checksum'] if i] operation = accepted['operation'][-1] if not flavor in freeze_flavors.keys(): output_objects.append({ 'object_type': 'error_text', 'text': 'Invalid freeze flavor: %s' % flavor }) return (output_objects, returnvalues.CLIENT_ERROR) title = freeze_flavors[flavor]['showfreeze_title'] title_entry['text'] = title output_objects.append({'object_type': 'header', 'text': title}) sorted_algos = supported_hash_algos() sorted_algos.sort() for checksum in checksum_list: if not checksum in sorted_algos: output_objects.append({ 'object_type': 'error_text', 'text': 'Invalid checksum algo(s): %s' % checksum }) return (output_objects, returnvalues.CLIENT_ERROR) if not configuration.site_enable_freeze: output_objects.append({ 'object_type': 'error_text', 'text': '''Freezing archives is disabled on this site. Please contact the site admins %s if you think it should be enabled. ''' % configuration.admin_email }) return (output_objects, returnvalues.OK) if not operation in allowed_operations: output_objects.append({ 'object_type': 'error_text', 'text': '''Operation must be one of %s.''' % ', '.join(allowed_operations) }) return (output_objects, returnvalues.OK) # We don't generally know checksum and edit status until AJAX returns hide_elems = {'edit': 'hidden', 'update': 'hidden', 'register': 'hidden'} for algo in sorted_algos: hide_elems['%ssum' % algo] = 'hidden' if operation in show_operations: # jquery support for tablesorter and confirmation dialog # table initially sorted by col. 0 (filename) refresh_call = 'ajax_showfreeze("%s", "%s", %s, "%s", "%s", "%s", "%s")' % \ (freeze_id, flavor, checksum_list, keyword_updating, keyword_final, configuration.site_freeze_doi_url, configuration.site_freeze_doi_url_field) table_spec = { 'table_id': 'frozenfilestable', 'sort_order': '[[0,0]]', 'refresh_call': refresh_call } (add_import, add_init, add_ready) = man_base_js(configuration, [table_spec]) if operation == "show": add_ready += '%s;' % refresh_call # Only show requested checksums for algo in sorted_algos: if algo in checksum_list: add_ready += """ $('.%ssum').show(); """ % checksum else: add_ready += """ $('.%ssum').hide(); """ % algo title_entry['script']['advanced'] += add_import title_entry['script']['init'] += add_init title_entry['script']['ready'] += add_ready output_objects.append({ 'object_type': 'html_form', 'text': man_base_html(configuration) }) output_objects.append({ 'object_type': 'table_pager', 'entry_name': 'frozen files', 'default_entries': default_pager_entries, 'refresh_button': False }) # Helper form for removes form_method = 'post' csrf_limit = get_csrf_limit(configuration) target_op = 'deletefreeze' csrf_token = make_csrf_token(configuration, form_method, target_op, client_id, csrf_limit) helper = html_post_helper( 'delfreeze', '%s.py' % target_op, { 'freeze_id': '__DYNAMIC__', 'flavor': '__DYNAMIC__', 'path': '__DYNAMIC__', 'target': TARGET_PATH, csrf_field: csrf_token }) output_objects.append({'object_type': 'html_form', 'text': helper}) # NB: the restrictions on freeze_id prevents illegal directory traversal if not is_frozen_archive(client_id, freeze_id, configuration): logger.error("%s: invalid freeze '%s': %s" % (op_name, client_id, freeze_id)) output_objects.append({ 'object_type': 'error_text', 'text': "'%s' is not an existing frozen archive!" % freeze_id }) return (output_objects, returnvalues.CLIENT_ERROR) if operation in list_operations: (load_status, freeze_dict) = get_frozen_archive(client_id, freeze_id, configuration, checksum_list) if not load_status: logger.error("%s: load failed for '%s': %s" % (op_name, freeze_id, freeze_dict)) output_objects.append({ 'object_type': 'error_text', 'text': 'Could not read details for "%s"' % freeze_id }) return (output_objects, returnvalues.SYSTEM_ERROR) if freeze_dict.get('FLAVOR', 'freeze') != flavor: logger.error("%s: flavor mismatch for '%s': %s vs %s" % (op_name, freeze_id, flavor, freeze_dict)) output_objects.append({ 'object_type': 'error_text', 'text': 'No such %s archive "%s"' % (flavor, freeze_id) }) return (output_objects, returnvalues.CLIENT_ERROR) # Allow edit if not in updating/final state and allow request DOI if # finalized and not a backup archive. freeze_state = freeze_dict.get('STATE', keyword_final) if freeze_state == keyword_updating: hide_elems['update'] = '' elif freeze_state != keyword_final: hide_elems['edit'] = '' elif flavor != 'backup' and configuration.site_freeze_doi_url and \ freeze_dict.get('PUBLISH_URL', ''): hide_elems['register'] = '' logger.debug("%s: build obj for '%s': %s" % (op_name, freeze_id, brief_freeze(freeze_dict))) output_objects.append( build_freezeitem_object(configuration, freeze_dict)) if operation == "show": # insert dummy placeholder to build table output_objects.append({ 'object_type': 'frozenarchive', 'id': freeze_id, 'creator': client_id, 'flavor': flavor, 'frozenfiles': [], 'name': 'loading ...', 'description': 'loading ...', 'created': 'loading ...', 'state': 'loading ...' }) if operation in show_operations: output_objects.append({ 'object_type': 'html_form', 'text': """<p> Show archive with file checksums - might take quite a while to calculate: </p>""" }) for algo in sorted_algos: output_objects.append({'object_type': 'html_form', 'text': '<p>'}) output_objects.append({ 'object_type': 'link', 'destination': "showfreeze.py?freeze_id=%s;flavor=%s;checksum=%s" % (freeze_id, flavor, algo), 'class': 'infolink iconspace genericbutton', 'title': 'View archive with %s checksums' % algo.upper(), 'text': 'Show with %s checksums' % algo.upper() }) output_objects.append({'object_type': 'html_form', 'text': '</p>'}) # We don't know state of archive in this case until AJAX returns # so we hide the section and let AJAX show it if relevant output_objects.append({ 'object_type': 'html_form', 'text': """ <div class='updatearchive %(update)s'> <p class='warn_message'> Archive is currently in the process of being updated. No further changes can be applied until running archive operations are completed. </p> </div> <div class='editarchive %(edit)s'> <p> You can continue inspecting and changing your archive until you're satisfied, then finalize it for actual persistent freezing. </p> <p>""" % hide_elems }) output_objects.append({ 'object_type': 'link', 'destination': "adminfreeze.py?freeze_id=%s;flavor=%s" % (freeze_id, flavor), 'class': 'editarchivelink iconspace genericbutton', 'title': 'Further modify your pending %s archive' % flavor, 'text': 'Edit archive' }) output_objects.append({'object_type': 'html_form', 'text': '</p>'}) form_method = 'post' target_op = 'createfreeze' csrf_limit = get_csrf_limit(configuration) csrf_token = make_csrf_token(configuration, form_method, target_op, client_id, csrf_limit) helper = html_post_helper( 'createfreeze', '%s.py' % target_op, { 'freeze_id': freeze_id, 'flavor': flavor, 'freeze_state': keyword_final, csrf_field: csrf_token }) output_objects.append({'object_type': 'html_form', 'text': helper}) output_objects.append({ 'object_type': 'link', 'destination': "javascript: confirmDialog(%s, '%s');" % ('createfreeze', 'Really finalize %s?' % freeze_id), 'class': 'finalizearchivelink iconspace genericbutton', 'title': 'Finalize %s archive to prevent further changes' % flavor, 'text': 'Finalize archive', }) output_objects.append({ 'object_type': 'html_form', 'text': """ </div> <div class='registerarchive %(register)s'> <p> You can register a <a href='http://www.doi.org/index.html'>Digital Object Identifier (DOI)</a> for finalized archives. This may be useful in case you want to reference the contents in a publication. </p> """ % hide_elems }) form_method = 'post' target_op = 'registerfreeze' csrf_limit = get_csrf_limit(configuration) csrf_token = make_csrf_token(configuration, form_method, target_op, client_id, csrf_limit) helper = html_post_helper( 'registerfreeze', configuration.site_freeze_doi_url, { 'freeze_id': freeze_id, 'freeze_author': client_id, configuration.site_freeze_doi_url_field: '__DYNAMIC__', 'callback_url': "%s.py" % target_op, csrf_field: csrf_token }) output_objects.append({'object_type': 'html_form', 'text': helper}) output_objects.append({ 'object_type': 'html_form', 'text': configuration.site_freeze_doi_text }) output_objects.append({ 'object_type': 'link', 'destination': "javascript: confirmDialog(%s, '%s');" % ('registerfreeze', 'Really request DOI for %s?' % freeze_id), 'class': 'registerarchivelink iconspace genericbutton', 'title': 'Register a DOI for %s archive %s' % (flavor, freeze_id), 'text': 'Request archive DOI', }) output_objects.append({ 'object_type': 'html_form', 'text': """ </div>""" }) output_objects.append({ 'object_type': 'html_form', 'text': """ <div class='vertical-spacer'></div>""" }) return (output_objects, returnvalues.OK)
% (op_name, share_id)) output_objects.append( {'object_type': 'error_text', 'text': """Import is only supported for directory sharelinks!"""}) return (output_objects, returnvalues.CLIENT_ERROR) elif not os.path.isdir(src_base): logger.error('%s called import with non-existant sharelink: %s' % (client_id, share_id)) output_objects.append( {'object_type': 'error_text', 'text': 'No such sharelink: %s' % share_id}) return (output_objects, returnvalues.CLIENT_ERROR) # Archive import if freeze_id is given - change to archive as src base if freeze_id: if not is_frozen_archive(client_id, freeze_id, configuration): logger.error('%s called with invalid freeze_id: %s' % (op_name, freeze_id)) output_objects.append( {'object_type': 'error_text', 'text': 'Invalid archive ID: %s' % freeze_id}) return (output_objects, returnvalues.CLIENT_ERROR) target_dir = os.path.join(client_dir, freeze_id) src_base = os.path.abspath(os.path.join(configuration.freeze_home, target_dir)) + os.sep if not os.path.isdir(src_base): logger.error('%s called import with non-existant archive: %s' % (client_id, freeze_id)) output_objects.append( {'object_type': 'error_text', 'text': 'No such archive: %s' % freeze_id})
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) defaults = signature()[1] title_entry = find_entry(output_objects, 'title') label = "%s" % configuration.site_vgrid_label title_entry['text'] = "Create Archive" # NOTE: Delay header entry here to include freeze flavor # All non-file fields must be validated validate_args = dict([(key, user_arguments_dict.get(key, val)) for (key, val) in defaults.items()]) # IMPORTANT: we must explicitly inlude CSRF token validate_args[csrf_field] = user_arguments_dict.get(csrf_field, [ 'AllowMe']) (validate_status, accepted) = validate_input_and_cert( validate_args, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) flavor = accepted['flavor'][-1].strip() freeze_state = accepted['freeze_state'][-1].strip() if not safe_handler(configuration, 'post', op_name, client_id, get_csrf_limit(configuration), accepted): output_objects.append( {'object_type': 'error_text', 'text': '''Only accepting CSRF-filtered POST requests to prevent unintended updates''' }) return (output_objects, returnvalues.CLIENT_ERROR) if not flavor in freeze_flavors.keys(): output_objects.append({'object_type': 'error_text', 'text': 'Invalid freeze flavor: %s' % flavor}) return (output_objects, returnvalues.CLIENT_ERROR) if not freeze_state in freeze_flavors[flavor]['states'] + [keyword_auto]: output_objects.append({'object_type': 'error_text', 'text': 'Invalid freeze state: %s' % freeze_state}) return (output_objects, returnvalues.CLIENT_ERROR) title = freeze_flavors[flavor]['createfreeze_title'] output_objects.append({'object_type': 'header', 'text': title}) if not configuration.site_enable_freeze: output_objects.append({'object_type': 'text', 'text': '''Freezing archives is disabled on this site. Please contact the site admins %s if you think it should be enabled. ''' % configuration.admin_email}) return (output_objects, returnvalues.OK) # jquery support for confirmation on freeze (add_import, add_init, add_ready) = man_base_js(configuration, []) title_entry['script']['advanced'] += add_import title_entry['script']['init'] += add_init title_entry['script']['ready'] += add_ready output_objects.append({'object_type': 'html_form', 'text': man_base_html(configuration)}) freeze_id = accepted['freeze_id'][-1].strip() freeze_name = accepted['freeze_name'][-1].strip() freeze_description = accepted['freeze_description'][-1] freeze_author = accepted['freeze_author'][-1].strip() freeze_department = accepted['freeze_department'][-1].strip() freeze_organization = accepted['freeze_organization'][-1].strip() freeze_publish = accepted['freeze_publish'][-1].strip() do_publish = (freeze_publish.lower() in ('on', 'true', 'yes', '1')) # Share init of base meta with lookup of default state in freeze_flavors if not freeze_state or freeze_state == keyword_auto: freeze_state = freeze_flavors[flavor]['states'][0] freeze_meta = {'ID': freeze_id, 'STATE': freeze_state} # New archives must have name and description set if freeze_id == keyword_auto: logger.debug("creating a new %s archive for %s" % (flavor, client_id)) if not freeze_name or freeze_name == keyword_auto: freeze_name = '%s-%s' % (flavor, datetime.datetime.now()) if not freeze_description: if flavor == 'backup': freeze_description = 'manual backup archive created on %s' % \ datetime.datetime.now() else: output_objects.append( {'object_type': 'error_text', 'text': 'You must provide a description for the archive!'}) return (output_objects, returnvalues.CLIENT_ERROR) if flavor == 'phd' and (not freeze_author or not freeze_department): output_objects.append({'object_type': 'error_text', 'text': """ You must provide author and department for the thesis!"""}) return (output_objects, returnvalues.CLIENT_ERROR) freeze_meta.update( {'FLAVOR': flavor, 'NAME': freeze_name, 'DESCRIPTION': freeze_description, 'AUTHOR': freeze_author, 'DEPARTMENT': freeze_department, 'ORGANIZATION': freeze_organization, 'PUBLISH': do_publish}) elif is_frozen_archive(client_id, freeze_id, configuration): logger.debug("updating existing %s archive for %s" % (flavor, client_id)) # Update any explicitly provided fields (may be left empty on finalize) changes = {} if freeze_name and freeze_name != keyword_auto: changes['NAME'] = freeze_name if freeze_author: changes['AUTHOR'] = freeze_author if freeze_description: changes['DESCRIPTION'] = freeze_description if freeze_publish: changes['PUBLISH'] = do_publish logger.debug("updating existing %s archive for %s with: %s" % (flavor, client_id, changes)) logger.debug("publish is %s based on %s" % (do_publish, freeze_publish)) freeze_meta.update(changes) else: logger.error("no such %s archive for %s: %s" % (flavor, client_id, freeze_id)) output_objects.append({'object_type': 'error_text', 'text': """ Invalid archive ID %s - you must either create a new archive or edit an existing archive of yours!""" % freeze_id}) return (output_objects, returnvalues.CLIENT_ERROR) # Now parse and validate files to archive for name in defaults.keys(): if user_arguments_dict.has_key(name): del user_arguments_dict[name] (copy_files, copy_rejected) = parse_form_copy(user_arguments_dict, client_id, configuration) (move_files, move_rejected) = parse_form_move(user_arguments_dict, client_id, configuration) (upload_files, upload_rejected) = parse_form_upload(user_arguments_dict, client_id, configuration) if copy_rejected + move_rejected + upload_rejected: output_objects.append({'object_type': 'error_text', 'text': 'Errors parsing freeze files: %s' % '\n '.join(copy_rejected + move_rejected + upload_rejected)}) return (output_objects, returnvalues.CLIENT_ERROR) # NOTE: this may be a new or an existing pending archive, and it will fail # if archive is already under update (retval, retmsg) = create_frozen_archive(freeze_meta, copy_files, move_files, upload_files, client_id, configuration) if not retval: output_objects.append({'object_type': 'error_text', 'text': 'Error creating/updating archive: %s' % retmsg}) return (output_objects, returnvalues.SYSTEM_ERROR) # Make sure we have freeze_id and other updated fields freeze_meta.update(retmsg) freeze_id = freeze_meta['ID'] logger.info("%s: successful for '%s': %s" % (op_name, freeze_id, client_id)) # Return simple status mainly for use in scripting output_objects.append({'object_type': 'freezestatus', 'freeze_id': freeze_id, 'flavor': flavor, 'freeze_state': freeze_state}) publish_note = '' if freeze_state == keyword_pending: publish_hint = 'Preview published archive page in a new window/tab' publish_text = 'Preview publishing' output_objects.append({'object_type': 'text', 'text': """ Saved *preliminary* %s archive with ID %s . You can continue inspecting and changing it until you're satisfied, then finalize it for actual persistent freezing.""" % (flavor, freeze_id)}) else: publish_hint = 'View published archive page in a new window/tab' publish_text = 'Open published archive' output_objects.append({'object_type': 'text', 'text': 'Successfully froze %s archive with ID %s .' % (flavor, freeze_id)}) if do_publish: public_url = published_url(freeze_meta, configuration) output_objects.append({'object_type': 'text', 'text': ''}) output_objects.append({ 'object_type': 'link', 'destination': public_url, 'class': 'previewarchivelink iconspace genericbutton', 'title': publish_hint, 'text': publish_text, 'target': '_blank', }) output_objects.append({'object_type': 'text', 'text': ''}) # Always allow show archive output_objects.append({ 'object_type': 'link', 'destination': 'showfreeze.py?freeze_id=%s;flavor=%s' % (freeze_id, flavor), 'class': 'viewarchivelink iconspace genericbutton', 'title': 'View details about your %s archive' % flavor, 'text': 'View details', }) if freeze_state == keyword_pending: output_objects.append({'object_type': 'text', 'text': ''}) output_objects.append({ 'object_type': 'link', 'destination': 'adminfreeze.py?freeze_id=%s' % freeze_id, 'class': 'editarchivelink iconspace genericbutton', 'title': 'Further modify your pending %s archive' % flavor, 'text': 'Edit archive', }) output_objects.append({'object_type': 'text', 'text': ''}) output_objects.append({'object_type': 'html_form', 'text': """ <br/><hr/><br/> <p class='warn_message'>IMPORTANT: you still have to explicitly finalize your archive before you get the additional data integrity/persistance guarantees like tape archiving. </p>"""}) form_method = 'post' target_op = 'createfreeze' csrf_limit = get_csrf_limit(configuration) csrf_token = make_csrf_token(configuration, form_method, target_op, client_id, csrf_limit) helper = html_post_helper('createfreeze', '%s.py' % target_op, {'freeze_id': freeze_id, 'freeze_state': keyword_final, 'flavor': flavor, csrf_field: csrf_token}) output_objects.append({'object_type': 'html_form', 'text': helper}) output_objects.append({ 'object_type': 'link', 'destination': "javascript: confirmDialog(%s, '%s');" % ('createfreeze', 'Really finalize %s?' % freeze_id), 'class': 'finalizearchivelink iconspace genericbutton', 'title': 'Finalize %s archive to prevent further changes' % flavor, 'text': 'Finalize archive', }) return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = initialize_main_variables(client_id, op_header=False) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) flavor = accepted["flavor"][-1] if not flavor in freeze_flavors.keys(): output_objects.append({"object_type": "error_text", "text": "Invalid freeze flavor: %s" % flavor}) return (output_objects, returnvalues.CLIENT_ERROR) title = freeze_flavors[flavor]["showfreeze_title"] output_objects.append({"object_type": "header", "text": title}) title_entry = find_entry(output_objects, "title") title_entry["text"] = title if not configuration.site_enable_freeze: output_objects.append( { "object_type": "text", "text": """Freezing archives is disabled on this site. Please contact the Grid admins %s if you think it should be enabled. """ % configuration.admin_email, } ) return (output_objects, returnvalues.OK) # jquery support for tablesorter css_helpers = { "css_base": os.path.join(configuration.site_images, "css"), "skin_base": configuration.site_skin_base, } title_entry["style"] = themed_styles(configuration) title_entry["javascript"] = ( """ <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.pager.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.widgets.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> <script type="text/javascript" > $(document).ready(function() { // table initially sorted by col. 0 (filename) var sortOrder = [[0,1]]; // use image path for sorting if there is any inside var imgTitle = function(contents) { var key = $(contents).find("a").attr("class"); if (key == null) { key = $(contents).html(); } return key; } $("#frozenfilestable").tablesorter({widgets: ["zebra", "saveSort"], sortList:sortOrder, textExtraction: imgTitle }) .tablesorterPager({ container: $("#pager"), size: %s }); } ); </script> """ % default_pager_entries ) freeze_id = accepted["freeze_id"][-1] # NB: the restrictions on freeze_id prevents illegal directory traversal if not is_frozen_archive(freeze_id, configuration): logger.error("%s: invalid freeze '%s': %s" % (op_name, client_id, freeze_id)) output_objects.append( {"object_type": "error_text", "text": "'%s' is not an existing frozen archive!" % freeze_id} ) return (output_objects, returnvalues.CLIENT_ERROR) (load_status, freeze_dict) = get_frozen_archive(freeze_id, configuration) if not load_status: logger.error("%s: load failed for '%s': %s" % (op_name, freeze_id, freeze_dict)) output_objects.append({"object_type": "error_text", "text": 'Could not read details for "%s"' % freeze_id}) return (output_objects, returnvalues.SYSTEM_ERROR) if freeze_dict.get("FLAVOR", "freeze") != flavor: logger.error("%s: flavor mismatch for '%s': %s vs %s" % (op_name, freeze_id, flavor, freeze_dict)) output_objects.append({"object_type": "error_text", "text": 'No such %s archive "%s"' % (flavor, freeze_id)}) return (output_objects, returnvalues.CLIENT_ERROR) output_objects.append( {"object_type": "table_pager", "entry_name": "frozen files", "default_entries": default_pager_entries} ) output_objects.append(build_freezeitem_object(configuration, freeze_dict)) return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) title_entry = find_entry(output_objects, 'title') title_entry['text'] = 'Delete frozen archive' defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) flavor = accepted['flavor'][-1] if not safe_handler(configuration, 'post', op_name, client_id, get_csrf_limit(configuration), accepted): output_objects.append({ 'object_type': 'error_text', 'text': '''Only accepting CSRF-filtered POST requests to prevent unintended updates''' }) return (output_objects, returnvalues.CLIENT_ERROR) if not flavor in freeze_flavors.keys(): output_objects.append({ 'object_type': 'error_text', 'text': 'Invalid freeze flavor: %s' % flavor }) return (output_objects, returnvalues.CLIENT_ERROR) title = freeze_flavors[flavor]['deletefreeze_title'] output_objects.append({'object_type': 'header', 'text': title}) title_entry = find_entry(output_objects, 'title') title_entry['text'] = title if not configuration.site_enable_freeze: output_objects.append({ 'object_type': 'text', 'text': '''Freezing archives is disabled on this site. Please contact the site admins %s if you think it should be enabled. ''' % configuration.admin_email }) return (output_objects, returnvalues.OK) freeze_id = accepted['freeze_id'][-1] target = accepted['target'][-1] path_list = accepted['path'] if not target in valid_targets: output_objects.append({ 'object_type': 'error_text', 'text': 'Invalid delete freeze target: %s' % target }) return (output_objects, returnvalues.CLIENT_ERROR) # NB: the restrictions on freeze_id prevents illegal directory traversal if not is_frozen_archive(client_id, freeze_id, configuration): logger.error("%s: invalid freeze '%s': %s" % (op_name, client_id, freeze_id)) output_objects.append({ 'object_type': 'error_text', 'text': "No such frozen archive: '%s'" % freeze_id }) return (output_objects, returnvalues.CLIENT_ERROR) (load_status, freeze_dict) = get_frozen_archive(client_id, freeze_id, configuration, checksum_list=[]) if not load_status: logger.error("%s: load failed for '%s': %s" % (op_name, freeze_id, freeze_dict)) output_objects.append({ 'object_type': 'error_text', 'text': 'Could not read frozen archive details for %s' % freeze_id }) return (output_objects, returnvalues.SYSTEM_ERROR) # Make sure the frozen archive belongs to the user trying to delete it if client_id != freeze_dict['CREATOR']: logger.error("%s: illegal access attempt for '%s': %s" % (op_name, freeze_id, client_id)) output_objects.append({ 'object_type': 'error_text', 'text': 'You are not the owner of frozen archive "%s"' % freeze_id }) return (output_objects, returnvalues.CLIENT_ERROR) if freeze_dict.get('FLAVOR', 'freeze') != flavor: logger.error("%s: flavor mismatch for '%s': %s vs %s" % (op_name, freeze_id, flavor, freeze_dict)) output_objects.append({ 'object_type': 'error_text', 'text': 'No such %s archive "%s"' % (flavor, freeze_id) }) return (output_objects, returnvalues.CLIENT_ERROR) # Prevent user-delete of the frozen archive if configuration forbids it. # We exclude any archives in the pending intermediate freeze state. # Freeze admins are also excluded from the restrictions. state = freeze_dict.get('STATE', keyword_final) if state == keyword_updating: output_objects.append({ 'object_type': 'error_text', 'text': "Can't change %s archive %s which is currently being updated" % (flavor, freeze_id) }) output_objects.append({ 'object_type': 'link', 'destination': 'showfreeze.py?freeze_id=%s;flavor=%s' % (freeze_id, flavor), 'class': 'viewarchivelink iconspace genericbutton', 'title': 'View details about your %s archive' % flavor, 'text': 'View details', }) return (output_objects, returnvalues.CLIENT_ERROR) elif state == keyword_final and \ flavor in configuration.site_permanent_freeze and \ not client_id in configuration.site_freeze_admins: output_objects.append({ 'object_type': 'error_text', 'text': "Can't change %s archives like '%s' yourself due to site policy" % (flavor, freeze_id) }) return (output_objects, returnvalues.CLIENT_ERROR) client_dir = client_id_dir(client_id) user_archives = os.path.join(configuration.freeze_home, client_dir) # Please note that base_dir must end in slash to avoid access to other # user archive dirs if own name is a prefix of another archive name base_dir = os.path.abspath(os.path.join(user_archives, freeze_id)) + os.sep if target == TARGET_ARCHIVE: # Delete the entire freeze archive (del_status, msg) = delete_frozen_archive(freeze_dict, client_id, configuration) # If something goes wrong when trying to delete freeze archive # freeze_id, an error is displayed. if not del_status: logger.error("%s: failed for '%s': %s" % (op_name, freeze_id, msg)) output_objects.append({ 'object_type': 'error_text', 'text': 'Could not remove entire %s archive %s: %s' % (flavor, freeze_id, msg) }) return (output_objects, returnvalues.SYSTEM_ERROR) # If deletion of frozen archive freeze_id is successful, we just # return OK else: logger.info("%s: successful for '%s': %s" % (op_name, freeze_id, client_id)) output_objects.append({ 'object_type': 'text', 'text': 'Successfully deleted %s archive: "%s"' % (flavor, freeze_id) }) elif target == TARGET_PATH: # Delete individual files in non-final archive del_paths = [] for path in path_list: # IMPORTANT: path must be expanded to abs for proper chrooting server_path = os.path.join(base_dir, path) abs_path = os.path.abspath(server_path) if not valid_user_path(configuration, abs_path, base_dir, False): # Out of bounds! logger.warning( '%s tried to %s del restricted path %s ! ( %s)' % (client_id, op_name, abs_path, path)) output_objects.append({ 'object_type': 'error_text', 'text': 'Not allowed to delete %s - outside archive %s !' % (path, freeze_id) }) continue del_paths.append(path) (del_status, msg_list) = delete_archive_files(freeze_dict, client_id, del_paths, configuration) # If something goes wrong when trying to delete files from archive # freeze_id, an error is displayed. if not del_status: logger.error("%s: delete archive file(s) failed for '%s':\n%s" % (op_name, freeze_id, '\n'.join(msg_list))) output_objects.append({ 'object_type': 'error_text', 'text': 'Could not remove file(s) from archive %s: %s' % (freeze_id, '\n '.join(msg_list)) }) return (output_objects, returnvalues.SYSTEM_ERROR) # If deletion of files from archive freeze_id is successful, we just # return OK else: logger.info("%s: delete %d files successful for '%s': %s" % (op_name, len(path_list), freeze_id, client_id)) output_objects.append({ 'object_type': 'text', 'text': 'Successfully deleted %d file(s) from archive: "%s"' % (len(path_list), freeze_id) }) # Success - show link to overview output_objects.append({ 'object_type': 'link', 'destination': 'freezedb.py', 'class': 'infolink iconspace', 'title': 'Show archives', 'text': 'Show archives' }) return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) freeze_id = accepted['freeze_id'][-1] path = accepted['path'][-1] if not is_frozen_archive(client_id, freeze_id, configuration): output_objects.append({'object_type': 'error_text', 'text': '''No such archive %s owned by you''' % \ freeze_id}) return (output_objects, returnvalues.CLIENT_ERROR) client_dir = client_id_dir(client_id) # Please note that base_dir must end in slash to avoid access to other # user dirs when own name is a prefix of another user name base_dir = os.path.abspath( os.path.join(configuration.freeze_home, client_dir, freeze_id)) + os.sep # TODO: remove this legacy fall back when done migrating archives if not os.path.isdir(base_dir): base_dir = os.path.abspath( os.path.join(configuration.freeze_home, freeze_id)) + os.sep # Strip leading slashes to avoid join() throwing away prefix rel_path = path.lstrip(os.sep) # IMPORTANT: path must be expanded to abs for proper chrooting abs_path = os.path.abspath(os.path.join(base_dir, rel_path)) if not valid_user_path(configuration, abs_path, base_dir, True): output_objects.append({ 'object_type': 'error_text', 'text': '''You are not allowed to use paths outside the archive dir.''' }) return (output_objects, returnvalues.CLIENT_ERROR) logger.debug('reading archive private file %s' % abs_path) try: private_fd = open(abs_path, 'rb') entry = {'object_type': 'binary', 'data': private_fd.read()} logger.info('return %db from archive private file %s of size %db' % \ (len(entry['data']), abs_path, os.path.getsize(abs_path))) # Cut away all the usual web page formatting to show only contents # Insert explicit content type to make sure clients don't break download # early because they think it is plain text and find a bogus EOF in # binary data. (content_type, content_encoding) = mimetypes.guess_type(abs_path) if not content_type: content_type = 'application/octet-stream' output_objects = [{'object_type': 'start', 'headers': [ ('Content-Type', content_type), ('Content-Disposition', 'attachment; filename="%s";' % \ os.path.basename(abs_path)) ] }, entry, {'object_type': 'script_status'}, {'object_type': 'end'}] private_fd.close() except Exception, exc: logger.error('Error reading archive private file %s' % exc) output_objects.append({ 'object_type': 'error_text', 'text': 'Error reading archive private file %s' % rel_path }) return (output_objects, returnvalues.SYSTEM_ERROR)