示例#1
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id)
    output_objects.append({'object_type': 'text', 'text'
                          : '--------- Trying to Clean front end ----------'
                          })
    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)

    unique_resource_name = accepted['unique_resource_name'][-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 is_owner(client_id, unique_resource_name,
                    configuration.resource_home, logger):
        output_objects.append({'object_type': 'error_text', 'text'
                              : 'Failure: You must be an owner of '
                               + unique_resource_name
                               + ' to clean the front end!'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    exit_status = returnvalues.OK
    (status, msg) = stop_resource_frontend(unique_resource_name,
            configuration, logger)
    if not status:
        output_objects.append({'object_type': 'error_text', 'text'
                              : 'Problems stopping front end during clean: %s'
                               % msg})
        return (output_objects, returnvalues.CLIENT_ERROR)

    (status2, msg2) = clean_resource_frontend(unique_resource_name,
            configuration.resource_home, logger)
    if not status2:
        output_objects.append({'object_type': 'error_text', 'text'
                              : 'Problems cleaning front end during clean: %s'
                               % msg2})
        exit_status = returnvalues.SYSTEM_ERROR
    if status and status2:
        output_objects.append({'object_type': 'text', 'text'
                              : 'Clean front end success: Stop output: %s Clean output %s'
                               % (msg, msg2)})

    return (output_objects, exit_status)
示例#2
0
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)
    client_dir = client_id_dir(client_id)
    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 configuration.site_enable_jobs:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '''Job execution is not enabled on this system'''
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    status = returnvalues.OK

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Job Manager'
    user_settings = title_entry.get('user_settings', {})
    title_entry['style'] = css_tmpl(configuration, user_settings)
    csrf_map = {}
    method = 'post'
    limit = get_csrf_limit(configuration)
    for target_op in csrf_backends:
        csrf_map[target_op] = make_csrf_token(configuration, method, target_op,
                                              client_id, limit)
    (add_import, add_init, add_ready) = js_tmpl_parts(csrf_map)
    title_entry['script']['advanced'] += add_import
    title_entry['script']['init'] += add_init
    title_entry['script']['ready'] += add_ready

    output_objects.append({'object_type': 'header', 'text': 'Job Manager'})
    output_objects.append({
        'object_type': 'table_pager',
        'entry_name': 'jobs',
        'default_entries': default_pager_entries,
        'form_append': pager_append()
    })
    output_objects.append({'object_type': 'html_form', 'text': html_post()})

    return (output_objects, status)
示例#3
0
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, op_title=False)
    client_dir = client_id_dir(client_id)
    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)
    job_id_list = accepted['job_id']
    external_dict = mrslkeywords.get_keywords_dict(configuration)

    if not configuration.site_enable_jobs:
        output_objects.append({'object_type': 'error_text', 'text':
            '''Job execution is not enabled on this system'''})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    # 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.mrsl_files_dir,
                        client_dir)) + os.sep

    status = returnvalues.OK
    for job_id in job_id_list:

        # job = Job()

        filepath = os.path.join(base_dir, job_id)
        filepath += '.mRSL'

        (new_job_obj_status, new_job_obj) = \
            create_job_object_from_pickled_mrsl(filepath, logger,
                external_dict)
        if not new_job_obj_status:
            output_objects.append({'object_type': 'error_text', 'text'
                                  : new_job_obj})
            status = returnvalues.CLIENT_ERROR
        else:

            # return new_job_obj

            output_objects.append({'object_type': 'jobobj', 'jobobj'
                                  : new_job_obj})
    return (output_objects, status)
示例#4
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id)

    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)

    unique_resource_name = accepted['unique_resource_name'][-1]

    if not is_owner(client_id, unique_resource_name,
                    configuration.resource_home, logger):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'You must be an owner of %s to get the list of owners!' %
            unique_resource_name
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # is_owner incorporates unique_resource_name verification - no need to
    # specifically check for illegal directory traversal

    base_dir = os.path.abspath(
        os.path.join(configuration.resource_home,
                     unique_resource_name)) + os.sep
    owners_file = os.path.join(base_dir, 'owners')

    (list_status, msg) = list_items_in_pickled_list(owners_file, logger)
    if not list_status:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Could not get list of owners, reason: %s' % msg
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    output_objects.append({'object_type': 'list', 'list': msg})
    return (output_objects, returnvalues.OK)
示例#5
0
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,
                                  op_menu=client_id)
    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)

    # output_objects.append({'object_type': 'header', 'text': 'Welcome to %s!' %
    #                       configuration.short_title})

    # Generate and insert the page HTML
    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = '%s Home' % configuration.short_title

    # jquery support for AJAX saving

    (add_import, add_init, add_ready) = save_settings_js(configuration)
    add_init += '''
    function addApp() {
        $("#app-nav-container").hide();
        $("#add-app__window").show();
    }
    function closeAddApp() {
        console.log("close add app");
        $("#app-nav-container").show();
        $("#add-app__window").hide();
    }
    '''
    add_ready += '''
                load_tips("%s");
    ''' % configuration.site_tips_snippet_url
    title_entry['script']['advanced'] += add_import
    title_entry['script']['init'] += add_init
    title_entry['script']['ready'] += add_ready

    html = html_tmpl(configuration, client_id, title_entry)
    output_objects.append({'object_type': 'html_form', 'text': html})

    return (output_objects, returnvalues.OK)
示例#6
0
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, op_title=False, op_menu=client_id)
    # IMPORTANT: no title in init above so we MUST call it immediately here
    #            or basic styling will break e.g. on crashes.
    styles = themed_styles(configuration)
    scripts = themed_scripts(configuration, logged_in=False)
    output_objects.append({
        'object_type': 'title',
        'text': 'Auto Launch',
        'skipmenu': True,
        'style': styles,
        'script': scripts
    })
    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)

    user_settings = load_settings(client_id, configuration)
    # NOTE: loaded settings may be the boolean False rather than dict here
    if not user_settings:
        user_settings = {}

    default_page = user_settings.get('DEFAULT_PAGE', None)
    if default_page and default_page in menu_items:
        redirect_location = menu_items[default_page]['url']
    else:
        redirect_location = configuration.site_landing_page
    headers = [('Status', '302 Moved'), ('Location', redirect_location)]
    logger.debug("user settings and site landing page gave %s" %
                 redirect_location)
    output_objects.append({'object_type': 'start', 'headers': headers})
    output_objects.append({'object_type': 'script_status'})

    return (output_objects, returnvalues.OK)
示例#7
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id)
    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)

    vgrid_name = accepted['vgrid_name'][-1]

    # Validity of user and vgrid names is checked in this init function so
    # no need to worry about illegal directory traversal through variables

    (ret_val, msg,
     ret_variables) = init_vgrid_script_list(vgrid_name, client_id,
                                             configuration)
    if not ret_val:
        output_objects.append({'object_type': 'error_text', 'text': msg})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # list

    (list_status, msg) = vgrid_list(vgrid_name,
                                    'members',
                                    configuration,
                                    allow_missing=True)
    if not list_status:
        output_objects.append({
            'object_type': 'error_text',
            'text': '%s' % msg
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    output_objects.append({'object_type': 'list', 'list': msg})
    return (output_objects, returnvalues.OK)
示例#8
0
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)

    unique_res_names = accepted['unique_resource_name']

    if not configuration.site_enable_resources:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '''Resources are not enabled on this system'''
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    # prepare for confirm dialog, tablesort and toggling the views (css/js)

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = "Resource Administration"

    # jquery support for tablesorter and confirmation on request and leave
    # requests table initially sorted by 4, 3 (date first and with alphabetical
    # client ID)

    table_specs = [{
        'table_id': 'accessrequeststable',
        'pager_id': 'accessrequests_pager',
        'sort_order': '[[4,0],[3,0]]'
    }]
    (add_import, add_init, add_ready) = man_base_js(configuration, table_specs,
                                                    {'width': 600})
    add_init += '''
        var toggleHidden = function(classname) {
            // classname supposed to have a leading dot 
            $(classname).toggleClass("hidden");
        };
        /* helper for dynamic form input fields */
        function onOwnerInputChange() {
            makeSpareFields("#dynownerspares", "cert_id");
        }
    '''
    add_ready += '''
    /* init add owners form with dynamic input fields */
    onOwnerInputChange();
    $("#dynownerspares").on("blur", "input[name=cert_id]", 
        function(event) {
            //console.debug("in add owner blur handler");
            onOwnerInputChange();
        }
    );
    '''
    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)
    })

    form_method = 'post'
    csrf_limit = get_csrf_limit(configuration)
    fill_helpers = {
        'short_title': configuration.short_title,
        'vgrid_label': configuration.site_vgrid_label,
        'form_method': form_method,
        'csrf_field': csrf_field,
        'csrf_limit': csrf_limit
    }

    (re_stat, re_list) = list_runtime_environments(configuration)
    if not re_stat:
        logger.warning('Failed to load list of runtime environments')
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Error getting list of runtime environments'
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    output_objects.append({'object_type': 'header', 'text': 'Manage Resource'})

    output_objects.append({
        'object_type':
        'sectionheader',
        'text':
        '%(short_title)s Resources Owned' % fill_helpers
    })
    quick_links = [{
        'object_type':
        'text',
        'text':
        'Quick links to all your resources and individual management'
    }]
    quick_links.append({
        'object_type': 'html_form',
        'text': '<div class="hidden quicklinks">'
    })
    quick_links.append({
        'object_type': 'link',
        'destination': "javascript:toggleHidden('.quicklinks');",
        'class': 'removeitemlink iconspace',
        'title': 'Toggle view',
        'text': 'Hide quick links'
    })
    quick_links.append({'object_type': 'text', 'text': ''})

    quick_res = {}
    quick_links_index = len(output_objects)
    output_objects.append({'object_type': 'sectionheader', 'text': ''})

    owned = 0
    res_map = get_resource_map(configuration)
    for unique_resource_name in res_map.keys():
        if sandbox_resource(unique_resource_name):
            continue
        owner_list = res_map[unique_resource_name][OWNERS]
        resource_config = res_map[unique_resource_name][CONF]
        visible_res_name = res_map[unique_resource_name][RESID]
        if client_id in owner_list:
            quick_res[unique_resource_name] = {
                'object_type':
                'multilinkline',
                'links': [{
                    'object_type': 'link',
                    'destination':
                    '?unique_resource_name=%s' % unique_resource_name,
                    'class': 'adminlink iconspace',
                    'title': 'Manage %s' % unique_resource_name,
                    'text': 'Manage %s' % unique_resource_name,
                }, {
                    'object_type':
                    'link',
                    'destination':
                    'viewres.py?unique_resource_name=%s' % visible_res_name,
                    'class':
                    'infolink iconspace',
                    'title':
                    'View %s' % unique_resource_name,
                    'text':
                    'View %s' % unique_resource_name,
                }]
            }

            if unique_resource_name in unique_res_names:
                raw_conf_file = os.path.join(configuration.resource_home,
                                             unique_resource_name,
                                             'config.MiG')
                try:
                    filehandle = open(raw_conf_file, 'r')
                    raw_conf = filehandle.readlines()
                    filehandle.close()
                except:
                    raw_conf = ['']

                res_html = display_resource(client_id, unique_resource_name,
                                            raw_conf, resource_config,
                                            owner_list, re_list, configuration,
                                            fill_helpers)
                output_objects.append({
                    'object_type': 'html_form',
                    'text': res_html
                })

                # Pending requests

                target_op = "addresowner"
                csrf_token = make_csrf_token(configuration, form_method,
                                             target_op, client_id, csrf_limit)
                helper = html_post_helper(
                    target_op, "%s.py" % target_op, {
                        'unique_resource_name': unique_resource_name,
                        'cert_id': '__DYNAMIC__',
                        'request_name': '__DYNAMIC__',
                        csrf_field: csrf_token
                    })
                output_objects.append({
                    'object_type': 'html_form',
                    'text': helper
                })
                target_op = "rejectresreq"
                csrf_token = make_csrf_token(configuration, form_method,
                                             target_op, client_id, csrf_limit)
                helper = html_post_helper(
                    target_op, "%s.py" % target_op, {
                        'unique_resource_name': unique_resource_name,
                        'request_name': '__DYNAMIC__',
                        csrf_field: csrf_token
                    })
                output_objects.append({
                    'object_type': 'html_form',
                    'text': helper
                })

                request_dir = os.path.join(configuration.resource_home,
                                           unique_resource_name)
                request_list = []
                for req_name in list_access_requests(configuration,
                                                     request_dir):
                    req = load_access_request(configuration, request_dir,
                                              req_name)
                    if not req:
                        continue
                    if req.get('request_type', None) != "resourceowner":
                        logger.error(
                            "unexpected request_type %(request_type)s" % req)
                        continue
                    request_item = build_accessrequestitem_object(
                        configuration, req)
                    # Convert filename with exotic chars into url-friendly pure hex version
                    shared_args = {
                        "unique_resource_name": unique_resource_name,
                        "request_name": hexlify(req["request_name"])
                    }
                    accept_args, reject_args = {}, {}
                    accept_args.update(shared_args)
                    reject_args.update(shared_args)
                    if req['request_type'] == "resourceowner":
                        accept_args["cert_id"] = req["entity"]
                    request_item['acceptrequestlink'] = {
                        'object_type':
                        'link',
                        'destination':
                        "javascript: confirmDialog(%s, '%s', %s, %s);" %
                        ("addresowner",
                         "Accept %(target)s %(request_type)s request from %(entity)s"
                         % req, 'undefined', "{%s}" % ', '.join([
                             "'%s': '%s'" % pair
                             for pair in accept_args.items()
                         ])),
                        'class':
                        'addlink iconspace',
                        'title':
                        'Accept %(target)s %(request_type)s request from %(entity)s'
                        % req,
                        'text':
                        ''
                    }
                    request_item['rejectrequestlink'] = {
                        'object_type':
                        'link',
                        'destination':
                        "javascript: confirmDialog(%s, '%s', %s, %s);" %
                        ("rejectresreq",
                         "Reject %(target)s %(request_type)s request from %(entity)s"
                         % req, 'undefined', "%s" % reject_args),
                        'class':
                        'removelink iconspace',
                        'title':
                        'Reject %(target)s %(request_type)s request from %(entity)s'
                        % req,
                        'text':
                        ''
                    }

                    request_list.append(request_item)

                output_objects.append({
                    'object_type': 'sectionheader',
                    'text': "Pending Requests"
                })
                output_objects.append({
                    'object_type': 'table_pager',
                    'id_prefix': 'accessrequests_',
                    'entry_name': 'access requests',
                    'default_entries': default_pager_entries
                })
                output_objects.append({
                    'object_type': 'accessrequests',
                    'accessrequests': request_list
                })

                output_objects.append({
                    'object_type': 'sectionheader',
                    'text': 'Retire resource'
                })
                output_objects.append({
                    'object_type':
                    'text',
                    'text':
                    '''
Use the link below to permanently remove the resource from the grid after
stopping all units and the front end.
'''
                })
                target_op = "delres"
                csrf_token = make_csrf_token(configuration, form_method,
                                             target_op, client_id, csrf_limit)
                js_name = 'delres%s' % hexlify(unique_resource_name)
                helper = html_post_helper(
                    js_name, '%s.py' % target_op, {
                        'unique_resource_name': unique_resource_name,
                        csrf_field: csrf_token
                    })
                output_objects.append({
                    'object_type': 'html_form',
                    'text': helper
                })
                output_objects.append({
                    'object_type':
                    'link',
                    'destination':
                    "javascript: confirmDialog(%s, '%s');" %
                    (js_name, 'Really delete %s? (fails if it is busy)' %
                     unique_resource_name),
                    'class':
                    'removelink iconspace',
                    'title':
                    'Delete %s' % unique_resource_name,
                    'text':
                    'Delete %s' % unique_resource_name
                })
            owned += 1

    if owned == 0:
        output_objects.append({
            'object_type':
            'text',
            'text':
            'You are not listed as owner of any resources!'
        })
    else:
        sorted_links = quick_res.items()
        sorted_links.sort()
        for (res_id, link_obj) in sorted_links:
            quick_links.append(link_obj)

            # add new line

            quick_links.append({'object_type': 'text', 'text': ''})

        quick_links.append({
            'object_type': 'html_form',
            'text': '</div><div class="quicklinks">'
        })
        quick_links.append({
            'object_type': 'link',
            'destination': "javascript:toggleHidden('.quicklinks');",
            'class': 'additemlink iconspace',
            'title': 'Toggle view',
            'text': 'Show quick links'
        })
        quick_links.append({'object_type': 'html_form', 'text': '</div>'})

        output_objects = output_objects[:quick_links_index]\
            + quick_links + output_objects[quick_links_index:]

    return (output_objects, returnvalues.OK)
示例#9
0
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 runtime environment'
    output_objects.append({
        'object_type': 'header',
        'text': 'Delete runtime environment'
    })
    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)

    re_name = accepted['re_name'][-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 valid_dir_input(configuration.re_home, re_name):
        logger.warning(
            "possible illegal directory traversal attempt re_name '%s'" %
            re_name)
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Illegal runtime environment name: "%s"' % re_name
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Check whether re_name represents a runtime environment
    if not is_runtime_environment(re_name, configuration):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            "No such runtime environment: '%s'" % re_name
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    (re_dict, load_msg) = get_re_dict(re_name, configuration)
    if not re_dict:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Could not read runtime environment details for %s: %s' %
            (re_name, load_msg)
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    # Make sure the runtime environment belongs to the user trying to delete it
    if client_id != re_dict['CREATOR']:
        output_objects.append({'object_type': 'error_text', 'text': \
        'You are not the owner of runtime environment "%s"' % re_name})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Prevent delete if the runtime environment is used by any resources
    actives = resources_using_re(configuration, re_name)

    # If the runtime environment is active, an error message is printed, along
    # with a list of the resources using the runtime environment
    if actives:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            "Can't delete runtime environment '%s' in use by resources:" %
            re_name
        })
        output_objects.append({'object_type': 'list', 'list': actives})
        output_objects.append({
            'object_type': 'link',
            'destination': 'redb.py',
            'class': 'infolink iconspace',
            'title': 'Show runtime environments',
            'text': 'Show runtime environments'
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Delete the runtime environment
    (del_status, msg) = delete_runtimeenv(re_name, configuration)

    # If something goes wrong when trying to delete runtime environment
    # re_name, an error is displayed.
    if not del_status:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Could not remove %s runtime environment: %s' % (re_name, msg)
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    # If deletion of runtime environment re_name is successful, we just
    # return OK
    else:
        output_objects.append({
            'object_type':
            'text',
            'text':
            'Successfully deleted runtime environment: "%s"' % re_name
        })
        output_objects.append({
            'object_type': 'link',
            'destination': 'redb.py',
            'class': 'infolink iconspace',
            'title': 'Show runtime environments',
            'text': 'Show runtime environments'
        })
        return (output_objects, returnvalues.OK)
示例#10
0
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)
    client_dir = client_id_dir(client_id)
    defaults = signature()[1]
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
        # NOTE: path cannot use wildcards here
        typecheck_overrides={},
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    path = accepted['path'][-1]
    chosen_newline = accepted['newline'][-1]
    submitjob = accepted['submitjob'][-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 configuration.site_enable_jobs and submitjob:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '''Job execution is not enabled on this system'''
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    # 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.user_home, client_dir)) + os.sep

    # HTML spec dictates newlines in forms to be MS style (\r\n)
    # rather than un*x style (\n): change if requested.

    form_newline = '\r\n'
    allowed_newline = {'unix': '\n', 'mac': '\r', 'windows': '\r\n'}
    output_objects.append({
        'object_type': 'header',
        'text': 'Saving changes to edited file'
    })

    if not chosen_newline in allowed_newline.keys():
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Unsupported newline style supplied: %s (must be one of %s)' %
            (chosen_newline, ', '.join(allowed_newline.keys()))
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    saved_newline = allowed_newline[chosen_newline]

    # Check directory traversal attempts before actual handling to avoid
    # leaking information about file system layout while allowing consistent
    # error messages

    abs_path = ''
    unfiltered_match = glob.glob(base_dir + path)
    for server_path in unfiltered_match:
        # IMPORTANT: path must be expanded to abs for proper chrooting
        abs_path = os.path.abspath(server_path)
        if not valid_user_path(configuration, abs_path, base_dir, True):
            logger.warning('%s tried to %s restricted path %s ! (%s)' %
                           (client_id, op_name, abs_path, path))
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "Invalid path! (%s expands to an illegal path)" % path
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

    if abs_path == '':
        # IMPORTANT: path must be expanded to abs for proper chrooting
        abs_path = os.path.abspath(os.path.join(base_dir, path.lstrip(os.sep)))
        if not valid_user_path(configuration, abs_path, base_dir, True):
            logger.warning('%s tried to %s restricted path %s ! (%s)' %
                           (client_id, op_name, abs_path, path))
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "Invalid path! (%s expands to an illegal path)" % path
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

    if not check_write_access(abs_path, parent_dir=True):
        logger.warning('%s called without write access: %s' %
                       (op_name, abs_path))
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'cannot edit "%s": inside a read-only location!' % path
        })
        status = returnvalues.CLIENT_ERROR
        return (output_objects, returnvalues.CLIENT_ERROR)

    (owner, time_left) = acquire_edit_lock(abs_path, client_id)
    if owner != client_id:
        output_objects.append({
            'object_type': 'error_text',
            'text': "You don't have the lock for %s!" % path
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    try:
        fh = open(abs_path, 'w+')
        fh.write(user_arguments_dict['editarea'][0].replace(
            form_newline, saved_newline))
        fh.close()

        # everything ok

        output_objects.append({
            'object_type': 'text',
            'text': 'Saved changes to %s.' % path
        })
        logger.info('saved changes to %s' % path)
        release_edit_lock(abs_path, client_id)
    except Exception as exc:

        # Don't give away information about actual fs layout

        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '%s could not be written! (%s)' %
            (path, str(exc).replace(base_dir, ''))
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)
    if submitjob:
        output_objects.append({
            'object_type': 'text',
            'text': 'Submitting saved file to parser'
        })
        submitstatus = {'object_type': 'submitstatus', 'name': path}
        (new_job_status, msg, job_id) = new_job(abs_path, client_id,
                                                configuration, False, True)
        if not new_job_status:
            submitstatus['status'] = False
            submitstatus['message'] = msg
        else:
            submitstatus['status'] = True
            submitstatus['job_id'] = job_id

        output_objects.append({
            'object_type': 'submitstatuslist',
            'submitstatuslist': [submitstatus]
        })

    output_objects.append({
        'object_type': 'link',
        'destination': 'javascript:history.back()',
        'class': 'backlink iconspace',
        'title': 'Go back to previous page',
        'text': 'Back to previous page'
    })

    return (output_objects, returnvalues.OK)
示例#11
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id)
    client_dir = client_id_dir(client_id)
    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)

    patterns = accepted['job_id']

    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 configuration.site_enable_jobs:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '''Job execution is not enabled on this system'''
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    if not patterns:
        output_objects.append({
            'object_type': 'error_text',
            'text': 'No job_id specified!'
        })
        return (output_objects, returnvalues.NO_SUCH_JOB_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.mrsl_files_dir,
                        client_dir)) + os.sep

    filelist = []
    keywords_dict = mrslkeywords.get_keywords_dict(configuration)
    for pattern in patterns:
        pattern = pattern.strip()

        # Backward compatibility - all_jobs keyword should match all jobs

        if pattern == all_jobs:
            pattern = '*'

        # Check directory traversal attempts before actual handling to avoid
        # leaking information about file system layout while allowing
        # consistent error messages

        unfiltered_match = glob.glob(base_dir + pattern + '.mRSL')
        match = []
        for server_path in unfiltered_match:
            # IMPORTANT: path must be expanded to abs for proper chrooting
            abs_path = os.path.abspath(server_path)
            if not valid_user_path(configuration, abs_path, base_dir, True):

                # out of bounds - save user warning for later to allow
                # partial match:
                # ../*/* is technically allowed to match own files.

                logger.warning('%s tried to %s restricted path %s ! (%s)' %
                               (client_id, op_name, abs_path, pattern))
                continue

            # Insert valid job files in filelist for later treatment

            match.append(abs_path)

        # Now actually treat list of allowed matchings and notify if no
        # (allowed) match

        if not match:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '%s: You do not have any matching job IDs!' % pattern
            })
            status = returnvalues.CLIENT_ERROR
        else:
            filelist += match

    # resubmit is hard on the server

    if len(filelist) > 100:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Too many matching jobs (%s)!' % len(filelist)
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    resubmitobjs = []
    status = returnvalues.OK
    for filepath in filelist:
        mrsl_file = filepath.replace(base_dir, '')
        job_id = mrsl_file.replace('.mRSL', '')

        # ("Resubmitting job with job_id: %s" % job_id)

        resubmitobj = {'object_type': 'resubmitobj', 'job_id': job_id}

        mrsl_dict = unpickle(filepath, logger)
        if not mrsl_dict:
            resubmitobj['message'] = "No such job: %s (%s)" % (job_id,
                                                               mrsl_file)
            status = returnvalues.CLIENT_ERROR
            resubmitobjs.append(resubmitobj)
            continue

        resubmit_items = keywords_dict.keys()

        # loop selected keywords and create mRSL string

        resubmit_job_string = ''

        for dict_elem in resubmit_items:
            value = ''
            # Extract job value with fallback to default to support optional
            # fields
            job_value = mrsl_dict.get(dict_elem,
                                      keywords_dict[dict_elem]['Value'])
            if keywords_dict[dict_elem]['Type'].startswith(
                    'multiplekeyvalues'):
                for (elem_key, elem_val) in job_value:
                    if elem_key:
                        value += '%s=%s\n' % (str(elem_key).strip(),
                                              str(elem_val).strip())
            elif keywords_dict[dict_elem]['Type'].startswith('multiple'):
                for elem in job_value:
                    if elem:
                        value += '%s\n' % str(elem).rstrip()
            else:
                if str(job_value):
                    value += '%s\n' % str(job_value).rstrip()

            # Only insert keywords with an associated value

            if value:
                if value.rstrip() != '':
                    resubmit_job_string += '''::%s::
%s

''' % (dict_elem, value.rstrip())

        # save tempfile

        (filehandle, tempfilename) = \
            tempfile.mkstemp(dir=configuration.mig_system_files,
                             text=True)
        os.write(filehandle, resubmit_job_string)
        os.close(filehandle)

        # submit job the usual way

        (new_job_status, msg, new_job_id) = new_job(tempfilename, client_id,
                                                    configuration, False, True)
        if not new_job_status:
            resubmitobj['status'] = False
            resubmitobj['message'] = msg
            status = returnvalues.SYSTEM_ERROR
            resubmitobjs.append(resubmitobj)
            continue

            # o.out("Resubmit failed: %s" % msg)
            # o.reply_and_exit(o.ERROR)

        resubmitobj['status'] = True
        resubmitobj['new_job_id'] = new_job_id
        resubmitobjs.append(resubmitobj)

        # o.out("Resubmit successful: %s" % msg)
        # o.out("%s" % msg)

    output_objects.append({
        'object_type': 'resubmitobjs',
        'resubmitobjs': resubmitobjs
    })

    return (output_objects, status)
示例#12
0
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]
    status = returnvalues.OK
    output_objects.append({
        'object_type': 'header',
        'text': 'Add Resource Owner(s)'
    })
    (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)

    unique_resource_name = accepted['unique_resource_name'][-1].strip()
    cert_id_list = accepted['cert_id']
    request_name = unhexlify(accepted['request_name'][-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 is_owner(client_id, unique_resource_name,
                    configuration.resource_home, logger):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'You must be an owner of %s to add a new owner!' %
            unique_resource_name
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # is_owner incorporates unique_resource_name verification - no need to
    # specifically check for illegal directory traversal

    cert_id_added = []
    for cert_id in cert_id_list:
        cert_id = cert_id.strip()
        if not cert_id:
            continue
        if not is_user(cert_id, configuration.mig_server_home):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '%s is not a valid %s user!' %
                (cert_id, configuration.short_title)
            })
            status = returnvalues.CLIENT_ERROR
            continue

        # don't add if already an owner

        if resource_is_owner(unique_resource_name, cert_id, configuration):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '%s is already an owner of %s.' %
                (cert_id, unique_resource_name)
            })
            status = returnvalues.CLIENT_ERROR
            continue

        # Add owner

        (add_status, add_msg) = resource_add_owners(configuration,
                                                    unique_resource_name,
                                                    [cert_id])
        if not add_status:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Could not add new owner, reason: %s' % add_msg
            })
            status = returnvalues.SYSTEM_ERROR
            continue
        cert_id_added.append(cert_id)

    if request_name:
        request_dir = os.path.join(configuration.resource_home,
                                   unique_resource_name)
        if not delete_access_request(configuration, request_dir, request_name):
            logger.error("failed to delete owner request for %s in %s" % \
                         (unique_resource_name, request_name))
            output_objects.append({
                'object_type': 'error_text', 'text':
                'Failed to remove saved request for %s in %s!' % \
                (unique_resource_name, request_name)})

    if cert_id_added:
        output_objects.append({
            'object_type':
            'html_form',
            'text':
            'New owner(s)<br/>%s<br/>successfully added to %s!' %
            ('<br />'.join(cert_id_added), unique_resource_name)
        })
        cert_id_fields = ''
        for cert_id in cert_id_added:
            cert_id_fields += """<input type=hidden name=cert_id value='%s' />
""" % cert_id

        form_method = 'post'
        csrf_limit = get_csrf_limit(configuration)
        fill_helpers = {
            'res_id': unique_resource_name,
            'cert_id_fields': cert_id_fields,
            'any_protocol': any_protocol,
            'form_method': form_method,
            'csrf_field': csrf_field,
            'csrf_limit': csrf_limit
        }
        target_op = 'sendrequestaction'
        csrf_token = make_csrf_token(configuration, form_method, target_op,
                                     client_id, csrf_limit)
        fill_helpers.update({'target_op': target_op, 'csrf_token': csrf_token})
        output_objects.append({
            'object_type':
            'html_form',
            'text':
            """
<form method='%(form_method)s' action='%(target_op)s.py'>
<input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
<input type=hidden name=request_type value='resourceaccept' />
<input type=hidden name=unique_resource_name value='%(res_id)s' />
%(cert_id_fields)s
<input type=hidden name=protocol value='%(any_protocol)s' />
<table>
<tr>
<td class='title'>Custom message to user(s)</td>
</tr>
<tr>
<td><textarea name=request_text cols=72 rows=10>
We have granted you ownership access to our %(res_id)s resource.
You can access the resource administration page from the Resources page.

Regards, the %(res_id)s resource owners
</textarea></td>
</tr>
<tr>
<td><input type='submit' value='Inform user(s)' /></td>
</tr>
</table>
</form>
<br />
""" % fill_helpers
        })

    output_objects.append({'object_type': 'link', 'destination':
                           'resadmin.py?unique_resource_name=%s' % \
                           unique_resource_name, 'class':
                           'adminlink iconspace', 'title':
                           'Administrate resource', 'text': 'Manage resource'})
    return (output_objects, status)
示例#13
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id)
    client_dir = client_id_dir(client_id)
    defaults = signature()[1]
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
        # NOTE: path can use wildcards, dst and current_dir cannot
        typecheck_overrides={'path': valid_path_pattern},
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    flags = ''.join(accepted['flags'])
    pattern_list = accepted['path']
    dst = accepted['dst'][-1]
    current_dir = accepted['current_dir'][-1].lstrip(os.sep)

    # All paths are relative to current_dir

    pattern_list = [os.path.join(current_dir, i) for i in pattern_list]
    if dst:
        dst = os.path.join(current_dir, dst)

    # 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.user_home, client_dir)) + os.sep

    status = returnvalues.OK

    if verbose(flags):
        for flag in flags:
            output_objects.append({
                'object_type': 'text',
                'text': '%s using flag: %s' % (op_name, flag)
            })

    # IMPORTANT: path must be expanded to abs for proper chrooting
    abs_dir = os.path.abspath(
        os.path.join(base_dir, current_dir.lstrip(os.sep)))
    if not valid_user_path(configuration, abs_dir, base_dir, True):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            "You're not allowed to work in %s!" % current_dir
        })
        logger.warning('%s tried to %s restricted path %s ! (%s)' %
                       (client_id, op_name, abs_dir, current_dir))
        return (output_objects, returnvalues.CLIENT_ERROR)

    if verbose(flags):
        output_objects.append({
            'object_type': 'text',
            'text': "working in %s" % current_dir
        })

    if dst:
        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)

        # NOTE: dst already incorporates current_dir prefix here
        # IMPORTANT: path must be expanded to abs for proper chrooting
        abs_dest = os.path.abspath(os.path.join(base_dir, dst))
        logger.info('chksum in %s' % abs_dest)

        # Don't use abs_path in output as it may expose underlying
        # fs layout.

        relative_dest = abs_dest.replace(base_dir, '')
        if not valid_user_path(configuration, abs_dest, base_dir, True):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "Invalid path! (%s expands to an illegal path)" % dst
            })
            logger.warning('%s tried to %s restricted path %s !(%s)' %
                           (client_id, op_name, abs_dest, dst))
            return (output_objects, returnvalues.CLIENT_ERROR)
        if not check_write_access(abs_dest, parent_dir=True):
            logger.warning('%s called without write access: %s' %
                           (op_name, abs_dest))
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'cannot checksum to "%s": inside a read-only location!' %
                relative_dest
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

    all_lines = []
    for pattern in pattern_list:

        # Check directory traversal attempts before actual handling to avoid
        # leaking information about file system layout while allowing
        # consistent error messages

        unfiltered_match = glob.glob(base_dir + pattern)
        match = []
        for server_path in unfiltered_match:
            # IMPORTANT: path must be expanded to abs for proper chrooting
            abs_path = os.path.abspath(server_path)
            if not valid_user_path(configuration, abs_path, base_dir, True):

                # out of bounds - save user warning for later to allow
                # partial match:
                # ../*/* is technically allowed to match own files.

                logger.warning('%s tried to %s restricted path %s ! (%s)' %
                               (client_id, op_name, abs_path, pattern))
                continue
            match.append(abs_path)

        # Now actually treat list of allowed matchings and notify if no
        # (allowed) match

        if not match:
            output_objects.append({
                'object_type': 'file_not_found',
                'name': pattern
            })
            status = returnvalues.FILE_NOT_FOUND

        # NOTE: we produce output matching an invocation of:
        # du -aL --apparent-size --block-size=1 PATH [PATH ...]
        filedus = []
        summarize_output = summarize(flags)
        for abs_path in match:
            if invisible_path(abs_path):
                continue
            relative_path = abs_path.replace(base_dir, '')
            # cache accumulated sub dir sizes - du sums into parent dir size
            dir_sizes = {}
            try:
                # Assume a directory to walk
                for (root, dirs, files) in walk(abs_path,
                                                topdown=False,
                                                followlinks=True):
                    if invisible_path(root):
                        continue
                    dir_bytes = 0
                    for name in files:
                        real_file = os.path.join(root, name)
                        if invisible_path(real_file):
                            continue
                        relative_file = real_file.replace(base_dir, '')
                        size = os.path.getsize(real_file)
                        dir_bytes += size
                        if not summarize_output:
                            filedus.append({
                                'object_type': 'filedu',
                                'name': relative_file,
                                'bytes': size
                            })
                    for name in dirs:
                        real_dir = os.path.join(root, name)
                        if invisible_path(real_dir):
                            continue
                        dir_bytes += dir_sizes[real_dir]
                    relative_root = root.replace(base_dir, '')
                    dir_bytes += os.path.getsize(root)
                    dir_sizes[root] = dir_bytes
                    if root == abs_path or not summarize_output:
                        filedus.append({
                            'object_type': 'filedu',
                            'name': relative_root,
                            'bytes': dir_bytes
                        })
                if os.path.isfile(abs_path):
                    # Fall back to plain file where walk is empty
                    size = os.path.getsize(abs_path)
                    filedus.append({
                        'object_type': 'filedu',
                        'name': relative_path,
                        'bytes': size
                    })
            except Exception as exc:
                output_objects.append({
                    'object_type':
                    'error_text',
                    'text':
                    "%s: '%s': %s" % (op_name, relative_path, exc)
                })
                logger.error("%s: failed on '%s': %s" %
                             (op_name, relative_path, exc))
                status = returnvalues.SYSTEM_ERROR
                continue
        if dst:
            all_lines += [
                '%(bytes)d\t\t%(name)s\n' % entry for entry in filedus
            ]
        else:
            output_objects.append({
                'object_type': 'filedus',
                'filedus': filedus
            })

    if dst and not write_file(''.join(all_lines), abs_dest, logger):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            "failed to write disk use to %s" % relative_dest
        })
        logger.error("writing disk use to %s for %s failed" %
                     (abs_dest, client_id))
        status = returnvalues.SYSTEM_ERROR

    return (output_objects, status)
示例#14
0
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)

    status = returnvalues.OK
    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 configuration.site_enable_jobs:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '''Job execution is not enabled on this system'''
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Manage jobs'
    output_objects.append({'object_type': 'header', 'text': 'Manage Jobs'})
    output_objects.append({
        'object_type': 'sectionheader',
        'text': 'View status of all submitted jobs'
    })
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        '''
<form method="get" action="jobstatus.py">
Sort by modification time: <input type="radio" name="flags" value="sv" />yes
<input type="radio" name="flags" checked="checked" value="vi" />no<br />
<input type="hidden" name="job_id" value="*" />
<input type="hidden" name="output_format" value="html" />
<input type="submit" value="Show All" />
</form>
    '''
    })
    output_objects.append({
        'object_type': 'sectionheader',
        'text': 'View status of individual jobs'
    })
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        '''
Filter job IDs (* and ? wildcards are supported)<br />
<form method="get" action="jobstatus.py">
Job ID: <input type="text" name="job_id" value="*" size="30" /><br />
Show only <input type="text" name="max_jobs" size="6" value=5 /> first matching jobs<br />
Sort by modification time: <input type="radio" name="flags" checked="checked" value="vsi" />yes
<input type="radio" name="flags" value="vi" />no<br />
<input type="hidden" name="output_format" value="html" />
<input type="submit" value="Show" />
</form>
    '''
    })
    output_objects.append({
        'object_type': 'sectionheader',
        'text': 'Resubmit job'
    })
    form_method = 'post'
    csrf_limit = get_csrf_limit(configuration)
    fill_helpers = {
        'short_title': configuration.short_title,
        'form_method': form_method,
        'csrf_field': csrf_field,
        'csrf_limit': csrf_limit
    }
    target_op = 'resubmit'
    csrf_token = make_csrf_token(configuration, form_method, target_op,
                                 client_id, csrf_limit)
    fill_helpers.update({'target_op': target_op, 'csrf_token': csrf_token})
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        '''
<form method="%(form_method)s" action="%(target_op)s.py">
<input type="hidden" name="%(csrf_field)s" value="%(csrf_token)s" />
Job ID: <input type="text" name="job_id" size="30" /><br />
<input type="hidden" name="output_format" value="html" />
<input type="submit" value="Submit" />
</form>
    ''' % fill_helpers
    })
    output_objects.append({
        'object_type': 'sectionheader',
        'text': 'Freeze pending job'
    })
    target_op = 'jobaction'
    csrf_token = make_csrf_token(configuration, form_method, target_op,
                                 client_id, csrf_limit)
    fill_helpers.update({'target_op': target_op, 'csrf_token': csrf_token})
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        '''
<form method="%(form_method)s" action="%(target_op)s.py">
<input type="hidden" name="%(csrf_field)s" value="%(csrf_token)s" />
Job ID: <input type="text" name="job_id" size="30" /><br />
<input type="hidden" name="action" value="freeze" />
<input type="hidden" name="output_format" value="html" />
<input type="submit" value="Freeze job" />
</form>
    ''' % fill_helpers
    })
    output_objects.append({
        'object_type': 'sectionheader',
        'text': 'Thaw frozen job'
    })
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        '''
<form method="%(form_method)s" action="%(target_op)s.py">
<input type="hidden" name="%(csrf_field)s" value="%(csrf_token)s" />
Job ID: <input type="text" name="job_id" size="30" /><br />
<input type="hidden" name="action" value="thaw" />
<input type="hidden" name="output_format" value="html" />
<input type="submit" value="Thaw job" />
</form>
    ''' % fill_helpers
    })
    output_objects.append({
        'object_type': 'sectionheader',
        'text': 'Cancel pending or executing job'
    })
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        '''
<form method="%(form_method)s" action="%(target_op)s.py">
<input type="hidden" name="%(csrf_field)s" value="%(csrf_token)s" />
Job ID: <input type="text" name="job_id" size="30" /><br />
<input type="hidden" name="action" value="cancel" />
<input type="hidden" name="output_format" value="html" />
<input type="submit" value="Cancel job" />
</form>
    ''' % fill_helpers
    })
    output_objects.append({
        'object_type': 'sectionheader',
        'text': 'Request live I/O'
    })
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        '''
<form method="get" action="liveio.py">
Job ID: <input type="text" name="job_id" size="30" /><br />
<input type="hidden" name="output_format" value="html" />
<input type="submit" value="Request" />
</form>
<br />
    '''
    })
    return (output_objects, status)
示例#15
0
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'] = "Remove %s Trigger" % label
    output_objects.append({'object_type': 'header', 'text'
                          : 'Remove %s Trigger' % label})
    (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)

    vgrid_name = accepted['vgrid_name'][-1]
    rule_id = accepted['rule_id'][-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)

    logger.info("rmvgridtrigger %s %s" % (vgrid_name, rule_id))

    # Validity of user and vgrid names is checked in this init function so
    # no need to worry about illegal directory traversal through variables

    (ret_val, msg, ret_variables) = \
        init_vgrid_script_add_rem(vgrid_name, client_id,
                                  rule_id, 'trigger',
                                  configuration)
    if not ret_val:
        output_objects.append({'object_type': 'error_text', 'text': msg})
        return (output_objects, returnvalues.CLIENT_ERROR)
    elif msg:

        # In case of warnings, msg is non-empty while ret_val remains True

        output_objects.append({'object_type': 'warning', 'text': msg})

    # if we get here user is either vgrid owner or has rule ownership

    # can't remove if not a participant

    if not vgrid_is_trigger(vgrid_name, rule_id, configuration, recursive=False):
        output_objects.append({'object_type': 'error_text', 'text':
                               '%s is not a trigger in %s %s.' % \
                               (rule_id, vgrid_name, label)})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # remove

    (rm_status, rm_msg) = vgrid_remove_triggers(configuration, vgrid_name,
                                                 [rule_id])
    if not rm_status:
        logger.error('%s failed to remove trigger: %s' % (client_id, rm_msg))
        output_objects.append({'object_type': 'error_text', 'text': rm_msg})
        output_objects.append({'object_type': 'error_text', 'text':
                               '''%(rule_id)s might be listed as a trigger of
this %(vgrid_label)s because it is a trigger of a parent %(vgrid_label)s.
Removal must be performed from the most significant %(vgrid_label)s
possible.''' % {'rule_id': rule_id, 'vgrid_label': label}})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    logger.info('%s removed trigger: %s' % (client_id, rule_id))
    output_objects.append({'object_type': 'text', 'text':
                           'Trigger %s successfully removed from %s %s!'
                           % (rule_id, vgrid_name, label)})
    output_objects.append({'object_type': 'link', 'destination':
                           'vgridworkflows.py?vgrid_name=%s' % vgrid_name,
                           'text': 'Back to workflows for %s' % vgrid_name})
    return (output_objects, returnvalues.OK)
示例#16
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id)
    output_objects.append({
        'object_type': 'text',
        'text': '--------- Trying to RESTART exe ----------'
    })

    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)

    unique_resource_name = accepted['unique_resource_name'][-1]
    cputime = accepted['cputime'][-1]
    exe_name_list = accepted['exe_name']
    all = accepted['all'][-1].lower() == 'true'
    parallel = accepted['parallel'][-1].lower() == 'true'

    if not configuration.site_enable_resources:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '''Resources are not enabled on this system'''
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    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 is_owner(client_id, unique_resource_name,
                    configuration.resource_home, logger):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Failure: You must be an owner of ' + unique_resource_name +
            ' to restart the exe!'
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    exit_status = returnvalues.OK

    if all:
        exe_name_list = get_all_exe_names(unique_resource_name)

    # take action based on supplied list of exes

    if len(exe_name_list) == 0:
        output_objects.append({
            'object_type':
            'text',
            'text':
            "No exes specified and 'all' argument not set to true: Nothing to do!"
        })

    workers = []
    task_list = []
    for exe_name in exe_name_list:
        task = Worker(target=stop_resource_exe,
                      args=(unique_resource_name, exe_name,
                            configuration.resource_home, logger))
        workers.append((exe_name, [task]))
        task_list.append(task)
        throttle_max_concurrent(task_list)
        task.start()
        if not parallel:
            task.join()

    # Complete each stop thread before launching corresponding start threads

    for (exe_name, task_list) in workers:

        # We could optimize with non-blocking join here but keep it simple for now
        # as final result will need to wait for slowest member anyway

        task_list[0].join()
        task = Worker(target=start_resource_exe,
                      args=(unique_resource_name, exe_name,
                            configuration.resource_home, int(cputime), logger))
        task_list.append(task)
        throttle_max_concurrent(task_list)
        task.start()
        if not parallel:
            task.join()

    for (exe_name, task_list) in workers:
        (status, msg) = task_list[0].finish()
        output_objects.append({
            'object_type': 'header',
            'text': 'Restart exe output:'
        })
        if not status:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Problems stopping exe during restart: %s' % msg
            })

        (status2, msg2) = task_list[1].finish()
        if not status2:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Problems starting exe during restart: %s' % msg2
            })
            exit_status = returnvalues.SYSTEM_ERROR
        if status and status2:
            output_objects.append({
                'object_type':
                'text',
                'text':
                'Restart exe success: Stop output: %s ; Start output: %s' %
                (msg, msg2)
            })

    return (output_objects, exit_status)
示例#17
0
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)

    status = returnvalues.OK
    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)

    output_objects.append({'object_type': 'header', 'text': 'Downloads'})
    output_objects.append({
        'object_type': 'html_form',
        'text': """
<div class="migcontent">
This page provides access to on-demand downloads of the %(site)s user scripts in all available formats.<br />
Simply pick your flavor of choice to generate the latest user scripts in your %(site)s home directory and as a zip file for easy download.<p>
In order to use the scripts your need the interpreter of choice (bash or python at the moment) and the
<a href='http://curl.haxx.se' class='urllink iconspace'>cURL</a> command line client.<br />
There's a tutorial with examples of all the commands available on the %(site)s page. The python version of the user scripts additionally includes a miglib python module, which may be used to incorporate %(site)s commands in your python applications.
</div>
""" % {
            'site': configuration.short_title
        }
    })
    output_objects.append({
        'object_type': 'sectionheader',
        'text': '%s User Scripts' % configuration.short_title
    })
    form_method = 'post'
    csrf_limit = get_csrf_limit(configuration)
    fill_helpers = {
        'short_title': configuration.short_title,
        'form_method': form_method,
        'csrf_field': csrf_field,
        'csrf_limit': csrf_limit
    }
    target_op = 'scripts'
    csrf_token = make_csrf_token(configuration, form_method, target_op,
                                 client_id, csrf_limit)
    fill_helpers.update({'target_op': target_op, 'csrf_token': csrf_token})

    output_objects.append({
        'object_type':
        'html_form',
        'text':
        """
<div class='migcontent'>
Generate %(short_title)s user scripts to manage jobs and files:<br/>
    <div class='row button-grid'>
        <div class='col-lg-4 left'>
            <form method='%(form_method)s' action='%(target_op)s.py'>
            <input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
            <input type='hidden' name='output_format' value='html' />
            <input type='hidden' name='lang' value='python' />
            <input type='submit' value='python version' />
            </form>
        </div>
        <div class='col-lg-4 middle'>
            <form method='%(form_method)s' action='%(target_op)s.py'>
            <input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
            <input type='hidden' name='output_format' value='html' />
            <input type='hidden' name='lang' value='sh' />
            <input type='submit' value='sh version' />
            </form>
        </div>
        <div class='col-lg-4 right'>
        
            <form method='%(form_method)s' action='%(target_op)s.py'>
            <input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
            <input type='hidden' name='output_format' value='html' />
            <input type='submit' value='all versions' />
            </form>
        </div>
    </div>
</div>
<br />
    """ % fill_helpers
    })
    output_objects.append({
        'object_type':
        'sectionheader',
        'text':
        '%s Resource Scripts' % configuration.short_title
    })
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        """
<div class='migcontent'>
Generate %(short_title)s scripts to administrate resources and vgrids:<br/>
<div class='row button-grid'>
        <div class='col-lg-4 left'>
            <form method='%(form_method)s' action='%(target_op)s.py'>
            <input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
            <input type='hidden' name='output_format' value='html' />
            <input type='hidden' name='lang' value='python' />
            <input type='hidden' name='flavor' value='resource' />
            <input type='submit' value='python version' />
            </form>
        </div>
        <div class='col-lg-4 middle'>
            <form method='%(form_method)s' action='%(target_op)s.py'>
            <input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
            <input type='hidden' name='output_format' value='html' />
            <input type='hidden' name='lang' value='sh' />
            <input type='hidden' name='flavor' value='resource' />
            <input type='submit' value='sh version' />
            </form>
        </div>
        <div class='col-lg-4 right'>
            <form method='%(form_method)s' action='%(target_op)s.py'>
            <input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
            <input type='hidden' name='output_format' value='html' />
            <input type='hidden' name='flavor' value='resource' />
            <input type='submit' value='all versions' />
            </form>
        </div>
    </div>
</div>
    """ % fill_helpers
    })

    return (output_objects, status)
示例#18
0
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)
    status = returnvalues.OK
    defaults = signature()[1]
    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Resource management'
    (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)

    show_sandboxes = (accepted['show_sandboxes'][-1] != 'false')
    operation = accepted['operation'][-1]
    caching = (accepted['caching'][-1].lower() in ('true', 'yes'))

    if not configuration.site_enable_resources:
        output_objects.append({'object_type': 'error_text', 'text':
                               '''Resources are not enabled on this system'''})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    if not operation in allowed_operations:
        output_objects.append({'object_type': 'text', 'text':
                               '''Operation must be one of %s.''' %
                               ', '.join(allowed_operations)})
        return (output_objects, returnvalues.OK)

    logger.info("%s %s begin for %s" % (op_name, operation, client_id))
    pending_updates = False
    if operation in show_operations:

        # jquery support for tablesorter and confirmation on delete
        # table initially sorted by col. 1 (admin), then 0 (name)

        # NOTE: We distinguish between caching on page load and forced refresh
        refresh_call = 'ajax_resman(%s)'
        table_spec = {'table_id': 'resourcetable', 'sort_order':
                      '[[1,0],[0,0]]', 'refresh_call': refresh_call % 'false'}
        (add_import, add_init, add_ready) = man_base_js(configuration,
                                                        [table_spec])
        if operation == "show":
            add_ready += '%s;' % refresh_call % 'true'
        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': 'header', 'text':
                               'Available Resources'})

        output_objects.append({'object_type': 'sectionheader', 'text':
                               'Resources available on this server'})
        output_objects.append({'object_type': 'text', 'text': '''
All available resources are listed below with overall hardware specifications.
Any resources that you own will have a administration icon that you can click
to open resource management.
'''
                               })

        # Helper forms for requests and removes

        form_method = 'post'
        csrf_limit = get_csrf_limit(configuration)
        target_op = 'sendrequestaction'
        csrf_token = make_csrf_token(configuration, form_method, target_op,
                                     client_id, csrf_limit)
        helper = html_post_helper('reqresowner', '%s.py' % target_op,
                                  {'unique_resource_name': '__DYNAMIC__',
                                   'request_type': 'resourceowner',
                                   'request_text': '',
                                   csrf_field: csrf_token})
        output_objects.append({'object_type': 'html_form', 'text': helper})
        target_op = 'rmresowner'
        csrf_token = make_csrf_token(configuration, form_method, target_op,
                                     client_id, csrf_limit)
        helper = html_post_helper('rmresowner', '%s.py' % target_op,
                                  {'unique_resource_name': '__DYNAMIC__',
                                   'cert_id': client_id,
                                   csrf_field: csrf_token})
        output_objects.append({'object_type': 'html_form', 'text': helper})

        output_objects.append({'object_type': 'table_pager', 'entry_name':
                               'resources', 'default_entries':
                               default_pager_entries})

    resources = []
    if operation in list_operations:
        logger.info("get vgrid and resource map with caching %s" % caching)
        visible_res_confs = user_visible_res_confs(configuration, client_id,
                                                   caching)
        res_map = get_resource_map(configuration, caching)
        anon_map = anon_to_real_res_map(configuration.resource_home)

        # NOTE: use simple pending check if caching to avoid lock during update
        if caching:
            pending_updates = pending_vgrids_update(configuration) or \
                pending_resources_update(configuration)
        else:
            pending_updates = False
        if pending_updates:
            logger.debug("found pending cache updates: %s" % pending_updates)
        else:
            logger.debug("no pending cache updates")

        # Iterate through resources and show management for each one requested

        fields = ['PUBLICNAME', 'NODECOUNT', 'CPUCOUNT', 'MEMORY', 'DISK',
                  'ARCHITECTURE', 'SANDBOX', 'RUNTIMEENVIRONMENT']

        # NOTE: only resources that user is allowed to access are listed.
        #       Resource with neither exes nor stores are not shown to anyone
        #       but the owners. Similarly resources are not shown if all
        #       resource units solely participate in VGrids, which the user
        #       can't access.
        for visible_res_name in visible_res_confs.keys():
            unique_resource_name = visible_res_name
            if visible_res_name in anon_map.keys():
                unique_resource_name = anon_map[visible_res_name]

            if not show_sandboxes and sandbox_resource(unique_resource_name):
                continue
            res_obj = {'object_type': 'resource', 'name': visible_res_name}

            # NOTE: res may not yet have been added to res_map here
            if not res_map.get(unique_resource_name, None):
                logger.info("skip %s not yet in resource map" %
                            unique_resource_name)
                continue

            if client_id in res_map[unique_resource_name][OWNERS]:

                # Admin of resource when owner

                res_obj['resownerlink'] = {
                    'object_type': 'link',
                    'destination':
                    "javascript: confirmDialog(%s, '%s', %s, %s);"
                    % ('rmresowner', 'Really leave %s owners?' %
                       unique_resource_name,
                       'undefined', "{unique_resource_name: '%s'}" %
                       unique_resource_name),
                    'class': 'removelink iconspace',
                    'title': 'Leave %s owners' % unique_resource_name,
                    'text': ''}
                res_obj['resdetailslink'] = {
                    'object_type': 'link',
                    'destination':
                    'resadmin.py?unique_resource_name=%s'
                    % unique_resource_name,
                    'class': 'adminlink iconspace',
                    'title': 'Administrate %s' % unique_resource_name,
                    'text': ''}
            else:

                # link to become owner

                res_obj['resownerlink'] = {
                    'object_type': 'link',
                    'destination':
                    "javascript: confirmDialog(%s, '%s', '%s', %s);" %
                    ('reqresowner', "Request ownership of " +
                     visible_res_name + ":<br/>" +
                     "\nPlease write a message to the owners (field below).",
                     'request_text',
                     "{unique_resource_name: '%s'}" % visible_res_name),
                    'class': 'addlink iconspace',
                    'title': 'Request ownership of %s' % visible_res_name,
                    'text': ''}

                res_obj['resdetailslink'] = {
                    'object_type': 'link',
                    'destination':
                    'viewres.py?unique_resource_name=%s'
                    % visible_res_name,
                    'class': 'infolink iconspace',
                    'title': 'View detailed %s specs' %
                    visible_res_name,
                    'text': ''}

            # fields for everyone: public status
            for name in fields:
                res_obj[name] = res_map[unique_resource_name][CONF].get(name,
                                                                        '')
            # Use runtimeenvironment names instead of actual definitions
            res_obj['RUNTIMEENVIRONMENT'] = [i[0] for i in
                                             res_obj['RUNTIMEENVIRONMENT']]
            res_obj['RUNTIMEENVIRONMENT'].sort()
            resources.append(res_obj)

    if operation == "show":
        # insert dummy placeholder to build table
        res_obj = {'object_type': 'resource', 'name': ''}
        resources.append(res_obj)

    output_objects.append({'object_type': 'resource_list',
                           'pending_updates': pending_updates,
                           'resources': resources})

    if operation in show_operations:
        if configuration.site_enable_sandboxes:
            if show_sandboxes:
                output_objects.append({'object_type': 'link',
                                       'destination': '?show_sandboxes=false',
                                       'class': 'removeitemlink iconspace',
                                       'title': 'Hide sandbox resources',
                                       'text': 'Exclude sandbox resources',
                                       })

            else:
                output_objects.append({'object_type': 'link',
                                       'destination': '?show_sandboxes=true',
                                       'class': 'additemlink iconspace',
                                       'title': 'Show sandbox resources',
                                       'text': 'Include sandbox resources',
                                       })

        output_objects.append(
            {'object_type': 'sectionheader', 'text': 'Resource Status'})
        output_objects.append({'object_type': 'text',
                               'text': '''
Live resource status is available in the resource monitor page with all
%s/resources you can access
''' % configuration.site_vgrid_label})
        output_objects.append({
            'object_type': 'link',
            'destination': 'showvgridmonitor.py?vgrid_name=ALL',
            'class': 'monitorlink iconspace',
            'title': 'Show monitor with all resources you can access',
            'text': 'Global resource monitor',
        })

        output_objects.append({'object_type': 'sectionheader', 'text':
                               'Additional Resources'})
        output_objects.append({
            'object_type': 'text', 'text':
            'You can sign up spare or dedicated resources to the grid below.'
        })
        output_objects.append({'object_type': 'link',
                               'destination': 'resedit.py',
                               'class': 'addlink iconspace',
                               'title': 'Show sandbox resources',
                               'text': 'Create a new %s resource' %
                               configuration.short_title,
                               })
        output_objects.append({'object_type': 'sectionheader', 'text': ''})

        if configuration.site_enable_sandboxes:
            output_objects.append({
                'object_type': 'link',
                'destination': 'ssslogin.py',
                'class': 'adminlink iconspace',
                'title': 'Administrate and monitor your sandbox resources',
                'text': 'Administrate %s sandbox resources' %
                configuration.short_title})
            output_objects.append({'object_type': 'sectionheader', 'text': ''})
            output_objects.append({
                'object_type': 'link',
                'destination': 'oneclick.py',
                'class': 'sandboxlink iconspace',
                'title': 'Run a One-click resource in your browser',
                'text': 'Use this computer as One-click %s resource' %
                configuration.short_title})

    logger.info("%s %s end for %s" % (op_name, operation, client_id))
    return (output_objects, status)
示例#19
0
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)
    client_dir = client_id_dir(client_id)
    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)

    action = accepted['action'][-1]
    transfer_id = accepted['transfer_id'][-1]
    protocol = accepted['protocol'][-1]
    fqdn = accepted['fqdn'][-1]
    port = accepted['port'][-1]
    src_list = accepted['transfer_src']
    dst = accepted['transfer_dst'][-1]
    username = accepted['username'][-1]
    password = accepted['transfer_pw'][-1]
    key_id = accepted['key_id'][-1]
    # Skip empty exclude entries as they break backend calls
    exclude_list = [i for i in accepted['exclude'] if i]
    notify = accepted['notify'][-1]
    compress = accepted['compress'][-1]
    flags = accepted['flags']

    anon_checked, pw_checked, key_checked = '', '', ''
    if username:
        if key_id:
            key_checked = 'checked'
            init_login = "******"
        else:
            pw_checked = 'checked'
            init_login = "******"
    else:
        anon_checked = 'checked'
        init_login = "******"
    use_compress = False
    if compress.lower() in ("true", "1", "yes", "on"):
        use_compress = True

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Background Data Transfers'
    title_entry['container_class'] = 'fillwidth',

    # jquery support for tablesorter and confirmation on delete/redo:
    # datatransfer and key tables initially sorted by 0 (id) */

    datatransfer_spec = {
        'table_id': 'datatransferstable',
        'pager_id': 'datatransfers_pager',
        'sort_order': '[[0,0]]'
    }
    transferkey_spec = {
        'table_id': 'transferkeystable',
        'pager_id': 'transferkeys_pager',
        'sort_order': '[[0,0]]'
    }
    (add_import, add_init,
     add_ready) = man_base_js(configuration,
                              [datatransfer_spec, transferkey_spec])
    add_init += '''
    var fields = 0;
    var max_fields = 20;
    var src_input = "<label for=\'transfer_src\'>Source path(s)</label>";
    src_input += "<input id=\'src_FIELD\' type=text size=60 name=transfer_src value=\'PATH\' title=\'relative source path: local for exports and remote for imports\' />";
    src_input += "<input id=\'src_file_FIELD\' type=radio onclick=\'setSrcDir(FIELD, false);\' checked />Source file";
    src_input += "<input id=\'src_dir_FIELD\' type=radio onclick=\'setSrcDir(FIELD, true);\' />Source directory (recursive)";
    src_input += "<br />";
    var exclude_input = "<label for=\'exclude\'>Exclude path(s)</label>";
    exclude_input += "<input type=text size=60 name=exclude value=\'PATH\' title=\'relative path or regular expression to exclude\' />";
    exclude_input += "<br />";
    function addSource(path, is_dir) {
        if (path === undefined) {
            path = "";
        }
        if (is_dir === undefined) {
            is_dir = false;
        }
        if (fields < max_fields) {
            $("#srcfields").append(src_input.replace(/FIELD/g, fields).replace(/PATH/g, path));
            setSrcDir(fields, is_dir);
            fields += 1;
        } else {
            alert("Maximum " + max_fields + " source fields allowed!");
        }
    }
    function addExclude(path) {
        if (path === undefined) {
            path = "";
        }
        $("#excludefields").append(exclude_input.replace(/PATH/g, path));
    }
    function setDir(target, field_no, is_dir) {
        var id_prefix = "#"+target+"_";
        var input_id = id_prefix+field_no;
        var file_id = id_prefix+"file_"+field_no;
        var dir_id = id_prefix+"dir_"+field_no;
        var value = $(input_id).val();
        $(file_id).prop("checked", "");
        $(dir_id).prop("checked", "");
        if (is_dir) {
            $(dir_id).prop("checked", "checked");
            if(value.substr(-1) != "/") {
                value += "/";
            }
        } else {
            $(file_id).prop("checked", "checked");
            if(value.substr(-1) == "/") {
                value = value.substr(0, value.length - 1);
            }
        }
        $(input_id).val(value);
        return false;
    }
    function setSrcDir(field_no, is_dir) {
        return setDir("src", field_no, is_dir);
    }
    function setDstDir(field_no, is_dir) {
        return setDir("dst", field_no, is_dir);
    }
    function refreshSrcDir(field_no) {
        var dir_id = "#src_dir_"+field_no;
        var is_dir = $(dir_id).prop("checked");
        return setSrcDir(field_no, is_dir);
    }
    function refreshDstDir(field_no) {
        var dir_id = "#dst_dir_"+field_no;
        var is_dir = $(dir_id).prop("checked");
        return setDstDir(field_no, is_dir);
    }
    function setDefaultPort() {
        port_map = {"http": 80, "https": 443, "sftp": 22, "scp": 22, "ftp": 21,
                    "ftps": 21, "webdav": 80, "webdavs": 443, "rsyncssh": 22,
                    "rsyncd": 873};
        var protocol = $("#protocol_select").val();
        var port = port_map[protocol]; 
        if (port != undefined) {
            $("#port_input").val(port);
        } else {
            alert("no default port provided for "+protocol);
        }
    }
    function beforeSubmit() {
        for(var i=0; i < fields; i++) {
            refreshSrcDir(i);
        }
        refreshDstDir(0);
        // Proceed with submit
        return true;
    }
    function doSubmit() {
        $("#submit-request-transfer").click();
    }
    function enableLogin(method) {
        $("#anonymous_choice").prop("checked", "");
        $("#userpassword_choice").prop("checked", "");
        $("#userkey_choice").prop("checked", "");
        $("#username").prop("disabled", false);
        $("#password").prop("disabled", true);
        $("#key").prop("disabled", true);
        $("#login_fields").show();
        $("#password_entry").hide();
        $("#key_entry").hide();
        
        if (method == "password") {
            $("#userpassword_choice").prop("checked", "checked");
            $("#password").prop("disabled", false);
            $("#password_entry").show();
        } else if (method == "key") {
            $("#userkey_choice").prop("checked", "checked");
            $("#key").prop("disabled", false);
            $("#key_entry").show();
        } else {
            $("#anonymous_choice").prop("checked", "checked");
            $("#username").prop("disabled", true);
            $("#login_fields").hide();
        }
    }
    '''
    # Mangle ready handling to begin with dynamic init and end with tab init
    pre_ready = '''
        enableLogin("%s");
        ''' % init_login
    for src in src_list or ['']:
        pre_ready += '''
        addSource("%s", %s);
        ''' % (src, ("%s" % src.endswith('/')).lower())
    for exclude in exclude_list or ['']:
        pre_ready += '''
        addExclude("%s");
        ''' % exclude
    add_ready = '''
        %s
        %s
        /* NOTE: requires managers CSS fix for proper tab bar height */      
        $(".datatransfer-tabs").tabs();
        $("#logarea").scrollTop($("#logarea")[0].scrollHeight);
    ''' % (pre_ready, add_ready)
    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': 'header',
        'text': 'Manage background data transfers'
    })

    if not configuration.site_enable_transfers:
        output_objects.append({
            'object_type':
            'text',
            'text':
            '''Backgroung data transfers are disabled on
this site.
Please contact the site admins %s if you think they should be enabled.
''' % configuration.admin_email
        })
        return (output_objects, returnvalues.OK)

    logger.info('datatransfer %s from %s' % (action, client_id))

    if not action in valid_actions:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Invalid action "%s" (supported: %s)' %
            (action, ', '.join(valid_actions))
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    if action in post_actions:
        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)

    (load_status, transfer_map) = load_data_transfers(configuration, client_id)
    if not load_status:
        transfer_map = {}

    restrict_list = []
    for from_fqdn in configuration.site_transfers_from:
        restrict_list += [from_fqdn, socket.gethostbyname(from_fqdn)]
    restrict_str = 'from="%s",no-pty,' % ','.join(restrict_list)
    restrict_str += 'no-port-forwarding,no-agent-forwarding,no-X11-forwarding'
    restrict_template = '''
As usual it is a good security measure to prepend a <em>from</em> restriction
when you know the key will only be used from a single location.<br/>
In this case the keys will only ever be used from %s and will not need much
else, so the public key can be inserted in your authorized_keys file as:
<br/>
<p>
<textarea class="publickey" rows="5" readonly="readonly">%s %%s</textarea>
</p>
''' % (configuration.short_title, restrict_str)

    form_method = 'post'
    csrf_limit = get_csrf_limit(configuration)
    target_op = 'datatransfer'
    csrf_token = make_csrf_token(configuration, form_method, target_op,
                                 client_id, csrf_limit)
    if action in get_actions:
        datatransfers = []
        for (saved_id, transfer_dict) in transfer_map.items():
            transfer_item = build_transferitem_object(configuration,
                                                      transfer_dict)
            transfer_item['status'] = transfer_item.get('status', 'NEW')
            data_url = ''
            # NOTE: we need to urlencode any exotic chars in paths here
            if transfer_item['action'] == 'import':
                enc_path = quote(("%(dst)s" % transfer_item))
                data_url = "fileman.py?path=%s" % enc_path
            elif transfer_item['action'] == 'export':
                enc_paths = [quote(i) for i in transfer_item['src']]
                data_url = "fileman.py?path=" + ';path='.join(enc_paths)
            if data_url:
                transfer_item['viewdatalink'] = {
                    'object_type': 'link',
                    'destination': data_url,
                    'class': 'viewlink iconspace',
                    'title': 'View local component of %s' % saved_id,
                    'text': ''
                }
            transfer_item['viewoutputlink'] = {
                'object_type': 'link',
                'destination':
                "fileman.py?path=transfer_output/%s/" % saved_id,
                'class': 'infolink iconspace',
                'title': 'View status files for %s' % saved_id,
                'text': ''
            }
            # Edit is just a call to self with fillimport set
            args = [('action', 'fill%(action)s' % transfer_dict),
                    ('key_id', '%(key)s' % transfer_dict),
                    ('transfer_dst', '%(dst)s' % transfer_dict)]
            for src in transfer_dict['src']:
                args.append(('transfer_src', src))
            for exclude in transfer_dict.get('exclude', []):
                args.append(('exclude', exclude))
            for field in edit_fields:
                val = transfer_dict.get(field, '')
                args.append((field, val))
            transfer_args = urlencode(args, True)
            transfer_item['edittransferlink'] = {
                'object_type': 'link',
                'destination': "%s.py?%s" % (target_op, transfer_args),
                'class': 'editlink iconspace',
                'title': 'Edit or duplicate transfer %s' % saved_id,
                'text': ''
            }
            js_name = 'delete%s' % hexlify(saved_id)
            helper = html_post_helper(
                js_name, '%s.py' % target_op, {
                    'transfer_id': saved_id,
                    'action': 'deltransfer',
                    csrf_field: csrf_token
                })
            output_objects.append({'object_type': 'html_form', 'text': helper})
            transfer_item['deltransferlink'] = {
                'object_type':
                'link',
                'destination':
                "javascript: confirmDialog(%s, '%s');" %
                (js_name, 'Really remove %s?' % saved_id),
                'class':
                'removelink iconspace',
                'title':
                'Remove %s' % saved_id,
                'text':
                ''
            }
            js_name = 'redo%s' % hexlify(saved_id)
            helper = html_post_helper(
                js_name, '%s.py' % target_op, {
                    'transfer_id': saved_id,
                    'action': 'redotransfer',
                    csrf_field: csrf_token
                })
            output_objects.append({'object_type': 'html_form', 'text': helper})
            transfer_item['redotransferlink'] = {
                'object_type':
                'link',
                'destination':
                "javascript: confirmDialog(%s, '%s');" %
                (js_name, 'Really reschedule %s?' % saved_id),
                'class':
                'refreshlink iconspace',
                'title':
                'Reschedule %s' % saved_id,
                'text':
                ''
            }
            datatransfers.append(transfer_item)
        #logger.debug("found datatransfers: %s" % datatransfers)
        log_path = os.path.join(configuration.user_home,
                                client_id_dir(client_id), "transfer_output",
                                configuration.site_transfer_log)
        show_lines = 40
        log_lines = read_tail(log_path, show_lines, logger)
        available_keys = load_user_keys(configuration, client_id)
        if available_keys:
            key_note = ''
        else:
            key_note = '''No keys available - you can add a key for use in
transfers below.'''

        if action.endswith('import'):
            transfer_action = 'import'
        elif action.endswith('export'):
            transfer_action = 'export'
        else:
            transfer_action = 'unknown'

        import_checked, export_checked = 'checked', ''
        toggle_quiet, scroll_to_create = '', ''
        if action in ['fillimport', 'fillexport']:
            if quiet(flags):
                toggle_quiet = '''
<script>
 $("#wrap-tabs").hide();
 $("#quiet-mode-content").show();
</script>
'''
            scroll_to_create = '''
<script>
 $("html, body").animate({
  scrollTop: $("#createtransfer").offset().top
   }, 2000);
</script>
            '''
            if action == 'fillimport':
                import_checked = 'checked'
            elif action == 'fillexport':
                export_checked = 'checked'
                import_checked = ''

        fill_helpers = {
            'import_checked': import_checked,
            'export_checked': export_checked,
            'anon_checked': anon_checked,
            'pw_checked': pw_checked,
            'key_checked': key_checked,
            'transfer_id': transfer_id,
            'protocol': protocol,
            'fqdn': fqdn,
            'port': port,
            'username': username,
            'password': password,
            'key_id': key_id,
            'transfer_src_string': ', '.join(src_list),
            'transfer_src': src_list,
            'transfer_dst': dst,
            'exclude': exclude_list,
            'compress': use_compress,
            'notify': notify,
            'toggle_quiet': toggle_quiet,
            'scroll_to_create': scroll_to_create,
            'transfer_action': transfer_action,
            'form_method': form_method,
            'csrf_field': csrf_field,
            'csrf_limit': csrf_limit,
            'target_op': target_op,
            'csrf_token': csrf_token
        }

        # Make page with manage transfers tab and manage keys tab

        output_objects.append({
            'object_type':
            'html_form',
            'text':
            '''
    <div id="quiet-mode-content" class="hidden">
        <p>
        Accept data %(transfer_action)s of %(transfer_src_string)s from
        %(protocol)s://%(fqdn)s:%(port)s/ into %(transfer_dst)s ?
        </p>
        <p>
            <input type=button onClick="doSubmit();"
            value="Accept %(transfer_action)s" />
        </p>
    </div>
    <div id="wrap-tabs" class="datatransfer-tabs">
<ul>
<li><a href="#transfer-tab">Manage Data Transfers</a></li>
<li><a href="#keys-tab">Manage Transfer Keys</a></li>
</ul>
''' % fill_helpers
        })

        # Display external transfers, log and form to add new ones

        output_objects.append({
            'object_type': 'html_form',
            'text': '''
<div id="transfer-tab">
'''
        })

        output_objects.append({
            'object_type': 'sectionheader',
            'text': 'External Data Transfers'
        })
        output_objects.append({
            'object_type': 'table_pager',
            'id_prefix': 'datatransfers_',
            'entry_name': 'transfers',
            'default_entries': default_pager_entries
        })
        output_objects.append({
            'object_type': 'datatransfers',
            'datatransfers': datatransfers
        })
        output_objects.append({
            'object_type': 'sectionheader',
            'text': 'Latest Transfer Results'
        })
        output_objects.append({
            'object_type':
            'html_form',
            'text':
            '''
<textarea id="logarea" class="fillwidth" rows=5 readonly="readonly">%s</textarea>
''' % (''.join(log_lines))
        })
        output_objects.append({
            'object_type': 'sectionheader',
            'text': 'Create External Data Transfer'
        })
        transfer_html = '''
<table class="addexttransfer">
<tr><td>
Fill in the import/export data transfer details below to request a new
background data transfer task.<br/>  
Source must be a path without wildcard characters and it must be specifically
pointed out if the src is a directory. In that case recursive transfer will
automatically be used and otherwise the src is considered a single file, so it
will fail if that is not the case.<br/>  
Destination is a single location directory to transfer the data to. It is
considered in relation to your user home for <em>import</em> requests. Source
is similarly considered in relation to your user home in <em>export</em>
requests.<br/>
Destination is a always handled as a directory path to transfer source files
into.<br/>
<form method="%(form_method)s" action="%(target_op)s.py"
    onSubmit="return beforeSubmit();">
<input type="hidden" name="%(csrf_field)s" value="%(csrf_token)s" />
<fieldset id="transferbox">
<table id="createtransfer" class="addexttransfer">
<tr><td>
<label for="action">Action</label>
<input type=radio name=action %(import_checked)s value="import" />import data
<input type=radio name=action %(export_checked)s value="export" />export data
</td></tr>
<tr><td>
<label for="transfer_id">Optional Transfer ID / Name </label>
<input type=text size=60 name=transfer_id value="%(transfer_id)s"
    pattern="[a-zA-Z0-9._-]*"
    title="Optional ID string containing only ASCII letters and digits possibly with separators like hyphen, underscore and period" />
</td></tr>
<tr><td>
<label for="protocol">Protocol</label>
<select id="protocol_select" class="protocol-select themed-select html-select"
    name="protocol" onblur="setDefaultPort();">
'''
        # select requested protocol
        for (key, val) in valid_proto:
            if protocol == key:
                selected = 'selected'
            else:
                selected = ''
            transfer_html += '<option %s value="%s">%s</option>' % \
                             (selected, key, val)
        transfer_html += '''
</select>
</td></tr>
<tr><td>
<label for="fqdn">Host and port</label>
<input type=text size=37 name=fqdn value="%(fqdn)s" required
    pattern="[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+"
    title="A fully qualified domain name or Internet IP address for the remote location"/>
<input id="port_input" type=number step=1 min=1 max=65535 name=port
    value="%(port)s" required />
</td></tr>
<tr><td>
<label for="">Login method</label>
<input id="anonymous_choice" type=radio %(anon_checked)s
    onclick="enableLogin(\'anonymous\');" />
anonymous access
<input id="userpassword_choice" type=radio %(pw_checked)s
    onclick="enableLogin(\'password\');" />
login with password
<input id="userkey_choice" type=radio %(key_checked)s
    onclick="enableLogin(\'key\');" />
login with key
</td></tr>
<tr id="login_fields" style="display: none;"><td>
<label for="username">Username</label>
<input id="username" type=text size=60 name=username value="%(username)s"
    pattern="[a-zA-Z0-9._-]*"
    title="Optional username used to login on the remote site, if required" />
<br/>
<span id="password_entry">
<label for="transfer_pw">Password</label>
<input id="password" type=password size=60 name=transfer_pw value="" />
</span>
<span id="key_entry">
<label for="key_id">Key</label>
<select id="key" class="key-select themed-select html-select" name=key_id />
'''
        # select requested key
        for key_dict in available_keys:
            if key_dict['key_id'] == key_id:
                selected = 'selected'
            else:
                selected = ''
            transfer_html += '<option %s value="%s">%s</option>' % \
                             (selected, key_dict['key_id'], key_dict['key_id'])
            selected = ''
        transfer_html += '''
</select> %s
''' % key_note
        transfer_html += '''
</span>
</td></tr>
<tr><td>
<div id="srcfields">
<!-- NOTE: automatically filled by addSource function -->
</div>
<input id="addsrcbutton" type="button" onclick="addSource(); return false;"
    value="Add another source field" />
</td></tr>
<tr><td>
<label for="transfer_dst">Destination path</label>
<input id=\'dst_0\' type=text size=60 name=transfer_dst
    value="%(transfer_dst)s" required
    title="relative destination path: local for imports and remote for exports" />
<input id=\'dst_dir_0\' type=radio checked />Destination directory
<input id=\'dst_file_0\' type=radio disabled />Destination file<br />
</td></tr>
<tr><td>
<div id="excludefields">
<!-- NOTE: automatically filled by addExclude function -->
</div>
<input id="addexcludebutton" type="button" onclick="addExclude(); return false;"
    value="Add another exclude field" />
</td></tr>
<tr><td>
<label for="compress">Enable compression (leave unset except for <em>slow</em> sites)</label>
<input type=checkbox name=compress>
</td></tr>
<tr><td>
<label for="notify">Optional notify on completion (e.g. email address)</label>
<input type=text size=60 name=notify value=\'%(notify)s\'>
</td></tr>
<tr><td>
<span>
<input id="submit-request-transfer" type=submit value="Request transfer" />
<input type=reset value="Clear" />
</span>
</td></tr>
</table>
</fieldset>
</form>
</td>
</tr>
</table>
%(toggle_quiet)s
%(scroll_to_create)s
'''
        output_objects.append({
            'object_type': 'html_form',
            'text': transfer_html % fill_helpers
        })
        output_objects.append({
            'object_type': 'html_form',
            'text': '''
</div>
'''
        })

        # Display key management

        output_objects.append({
            'object_type': 'html_form',
            'text': '''
<div id="keys-tab">
'''
        })
        output_objects.append({
            'object_type': 'sectionheader',
            'text': 'Manage Data Transfer Keys'
        })
        key_html = '''
<form method="%(form_method)s" action="%(target_op)s.py">
<input type="hidden" name="%(csrf_field)s" value="%(csrf_token)s" />
<table class="managetransferkeys">
<tr><td>
'''
        transferkeys = []
        for key_dict in available_keys:
            key_item = build_keyitem_object(configuration, key_dict)
            saved_id = key_item['key_id']
            js_name = 'delete%s' % hexlify(saved_id)
            helper = html_post_helper(js_name, '%s.py' % target_op, {
                'key_id': saved_id,
                'action': 'delkey',
                csrf_field: csrf_token
            })
            output_objects.append({'object_type': 'html_form', 'text': helper})
            key_item['delkeylink'] = {
                'object_type':
                'link',
                'destination':
                "javascript: confirmDialog(%s, '%s');" %
                (js_name, 'Really remove %s?' % saved_id),
                'class':
                'removelink iconspace',
                'title':
                'Remove %s' % saved_id,
                'text':
                ''
            }
            transferkeys.append(key_item)

        output_objects.append({
            'object_type': 'table_pager',
            'id_prefix': 'transferkeys_',
            'entry_name': 'keys',
            'default_entries': default_pager_entries
        })
        output_objects.append({
            'object_type': 'transferkeys',
            'transferkeys': transferkeys
        })

        key_html += '''
Please copy the public key to your ~/.ssh/authorized_keys or
~/.ssh/authorized_keys2 file on systems where you want to login with the
corresponding key.<br/>
%s
</td></tr>
<tr><td>
Select a name below to create a new key for use in future transfers. The key is
generated and stored in a private storage area on %s, so that only the transfer
service can access and use it for your transfers.
</td></tr>
<tr><td>
<input type=hidden name=action value="generatekey" />
Key name:<br/>
<input type=text size=60 name=key_id value="" required pattern="[a-zA-Z0-9._-]+"
    title="internal name for the key when used in transfers. I.e. letters and digits separated only by underscores, periods and hyphens" />
<br/>
<input type=submit value="Generate key" />
</td></tr>
</table>
</form>
''' % (restrict_template % 'ssh-rsa AAAAB3NzaC...', configuration.short_title)
        output_objects.append({
            'object_type': 'html_form',
            'text': key_html % fill_helpers
        })
        output_objects.append({
            'object_type': 'html_form',
            'text': '''
</div>
'''
        })

        output_objects.append({
            'object_type': 'html_form',
            'text': '''
</div>
'''
        })
        return (output_objects, returnvalues.OK)
    elif action in transfer_actions:
        # NOTE: all path validation is done at run-time in grid_transfers
        transfer_dict = transfer_map.get(transfer_id, {})
        if action == 'deltransfer':
            if transfer_dict is None:
                output_objects.append({
                    'object_type':
                    'error_text',
                    'text':
                    'existing transfer_id is required for delete'
                })
                return (output_objects, returnvalues.CLIENT_ERROR)
            (save_status, _) = delete_data_transfer(configuration, client_id,
                                                    transfer_id, transfer_map)
            desc = "delete"
        elif action == 'redotransfer':
            if transfer_dict is None:
                output_objects.append({
                    'object_type':
                    'error_text',
                    'text':
                    'existing transfer_id is required for reschedule'
                })
                return (output_objects, returnvalues.CLIENT_ERROR)
            transfer_dict['status'] = 'NEW'
            (save_status, _) = update_data_transfer(configuration, client_id,
                                                    transfer_dict,
                                                    transfer_map)
            desc = "reschedule"
        else:
            if not fqdn:
                output_objects.append({
                    'object_type': 'error_text',
                    'text': 'No host address provided!'
                })
                return (output_objects, returnvalues.CLIENT_ERROR)
            if not [src for src in src_list if src] or not dst:
                output_objects.append({
                    'object_type':
                    'error_text',
                    'text':
                    'transfer_src and transfer_dst parameters '
                    'required for all data transfers!'
                })
                return (output_objects, returnvalues.CLIENT_ERROR)
            if protocol == "rsyncssh" and not key_id:
                output_objects.append({
                    'object_type':
                    'error_text',
                    'text':
                    'RSYNC over SSH is only supported with key!'
                })
                return (output_objects, returnvalues.CLIENT_ERROR)
            if not password and not key_id and protocol in warn_anon:
                output_objects.append({
                    'object_type':
                    'warning',
                    'text':
                    '''
%s transfers usually require explicit authentication with your credentials.
Proceeding as requested with anonymous login, but the transfer is likely to
fail.''' % valid_proto_map[protocol]
                })
            if key_id and protocol in warn_key:
                output_objects.append({
                    'object_type':
                    'warning',
                    'text':
                    '''
%s transfers usually only support authentication with username and password
rather than key. Proceeding as requested, but the transfer is likely to
fail if it really requires login.''' % valid_proto_map[protocol]
                })

            # Make pseudo-unique ID based on msec time since epoch if not given
            if not transfer_id:
                transfer_id = "transfer-%d" % (time.time() * 1000)
            if transfer_dict:
                desc = "update"
            else:
                desc = "create"

            if password:
                # We don't want to store password in plain text on disk
                password_digest = make_digest('datatransfer', client_id,
                                              password,
                                              configuration.site_digest_salt)
            else:
                password_digest = ''
            transfer_dict.update({
                'transfer_id': transfer_id,
                'action': action,
                'protocol': protocol,
                'fqdn': fqdn,
                'port': port,
                'username': username,
                'password_digest': password_digest,
                'key': key_id,
                'src': src_list,
                'dst': dst,
                'exclude': exclude_list,
                'compress': use_compress,
                'notify': notify,
                'status': 'NEW'
            })
            (save_status, _) = create_data_transfer(configuration, client_id,
                                                    transfer_dict,
                                                    transfer_map)
        if not save_status:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Error in %s data transfer %s: ' % (desc, transfer_id) +
                'save updated transfers failed!'
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

        output_objects.append({
            'object_type':
            'text',
            'text':
            '%sd transfer request %s.' % (desc.title(), transfer_id)
        })
        if action != 'deltransfer':
            output_objects.append({
                'object_type':
                'link',
                'destination':
                "fileman.py?path=transfer_output/%s/" % transfer_id,
                'title':
                'Transfer status and output',
                'text':
                'Transfer status and output folder'
            })
            output_objects.append({
                'object_type':
                'text',
                'text':
                '''
Please note that the status files only appear after the transfer starts, so it
may be empty now.
'''
            })
        logger.debug('datatransfer %s from %s done: %s' %
                     (action, client_id, transfer_dict))
    elif action in key_actions:
        if action == 'generatekey':
            (gen_status, pub) = generate_user_key(configuration, client_id,
                                                  key_id)
            if gen_status:
                output_objects.append({
                    'object_type':
                    'html_form',
                    'text':
                    '''
Generated new key with name %s and associated public key:<br/>
<textarea class="publickey" rows="5" readonly="readonly">%s</textarea>
<p>
Please copy it to your ~/.ssh/authorized_keys or ~/.ssh/authorized_keys2 file
on the host(s) where you want to use this key for background transfer login.
<br/>
%s
</p>
''' % (key_id, pub, restrict_template % pub)
                })
            else:
                output_objects.append({
                    'object_type':
                    'error_text',
                    'text':
                    '''
Key generation for name %s failed with error: %s''' % (key_id, pub)
                })
                return (output_objects, returnvalues.CLIENT_ERROR)
        elif action == 'delkey':
            pubkey = '[unknown]'
            available_keys = load_user_keys(configuration, client_id)
            for key_dict in available_keys:
                if key_dict['key_id'] == key_id:
                    pubkey = key_dict.get('public_key', pubkey)
            (del_status, msg) = delete_user_key(configuration, client_id,
                                                key_id)
            if del_status:
                output_objects.append({
                    'object_type':
                    'html_form',
                    'text':
                    '''
<p>
Deleted the key "%s" and the associated public key:<br/>
</p>
<textarea class="publickey" rows="5" readonly="readonly">%s</textarea>
<p>
You will no longer be able to use it in your data transfers and can safely
remove the public key from your ~/.ssh/authorized_keys* files on any hosts
where you may have previously added it.
</p>
''' % (key_id, pubkey)
                })
            else:
                output_objects.append({
                    'object_type':
                    'error_text',
                    'text':
                    '''
Key removal for name %s failed with error: %s''' % (key_id, msg)
                })
                return (output_objects, returnvalues.CLIENT_ERROR)
    else:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Invalid data transfer action: %s' % action
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    output_objects.append({
        'object_type': 'link',
        'destination': 'datatransfer.py',
        'text': 'Return to data transfers overview'
    })

    return (output_objects, returnvalues.OK)
示例#20
0
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]
    output_objects.append({
        'object_type': 'header',
        'text': 'Create runtime environment'
    })
    (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)
    re_template = accepted['re_template'][-1].upper().strip()
    software_entries = int(accepted['software_entries'][-1])
    environment_entries = int(accepted['environment_entries'][-1])
    testprocedure_entry = int(accepted['testprocedure_entry'][-1])

    template = {}
    if re_template:
        if not is_runtime_environment(re_template, configuration):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "re_template ('%s') is not a valid existing runtime env!" %
                re_template
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

        (template, msg) = get_re_dict(re_template, configuration)
        if not template:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Could not read re_template %s. %s' % (re_template, msg)
            })
            return (output_objects, returnvalues.SYSTEM_ERROR)

    # Override template fields if user loaded a template and modified the
    # required entries and chose update.
    # Use default of 1 sw, 1 env and 0 test or template setting otherwise
    if software_entries < 0:
        software_entries = len(template.get('SOFTWARE', [None]))
    if environment_entries < 0:
        environment_entries = len(template.get('ENVIRONMENTVARIABLE', [None]))
    if testprocedure_entry < 0:
        testprocedure_entry = len(template.get('TESTPROCEDURE', []))
    if 'SOFTWARE' in template:
        new_sw = template['SOFTWARE'][:software_entries]
        template['SOFTWARE'] = new_sw
    if 'ENVIRONMENTVARIABLE' in template:
        new_env = template['ENVIRONMENTVARIABLE'][:environment_entries]
        template['ENVIRONMENTVARIABLE'] = new_env
    if 'TESTPROCEDURE' in template:
        new_test = template['TESTPROCEDURE'][:testprocedure_entry]
        template['TESTPROCEDURE'] = new_test

    # Avoid DoS, limit number of software_entries

    if software_entries > max_software_entries:
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Maximum number of software_entries %s exceeded (%s)' % \
             (max_software_entries, software_entries)})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Avoid DoS, limit number of environment_entries

    if environment_entries > max_environment_entries:
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Maximum number of environment_entries %s exceeded (%s)' % \
             (max_environment_entries, environment_entries)})
        return (output_objects, returnvalues.CLIENT_ERROR)

    rekeywords_dict = get_keywords_dict()
    (list_status, ret) = list_runtime_environments(configuration)
    if not list_status:
        output_objects.append({'object_type': 'error_text', 'text': ret})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    output_objects.append({
        'object_type':
        'text',
        'text':
        'Use existing Runtime Environment as template'
    })

    html_form = \
        """<form method='get' action='adminre.py'>
    <select name='re_template'>
    <option value=''>None</option>
"""
    for existing_re in ret:
        html_form += "    <option value='%s'>%s</option>\n" % \
                     (existing_re, existing_re)
    html_form += """
    </select>
    <input type='submit' value='Get' />
</form>"""
    output_objects.append({'object_type': 'html_form', 'text': html_form})

    output_objects.append({
        'object_type':
        'text',
        'text':
        '''Note that a runtime environment can not be changed after creation
and it can only be removed if not in use by any resources, so please be careful
when filling in the details'''
    })
    output_objects.append({
        'object_type':
        'text',
        'text':
        '''Changing the number of software and environment entries removes
all data in the form, so please enter the correct values before entering any
information.'''
    })

    html_form = \
        """<form method='get' action='adminre.py'>
    <table>
"""
    html_form += """
<tr>
    <td>Number of needed software entries</td>
    <td><input type='number' name='software_entries' min=0 max=99
    minlength=1 maxlength=2 value='%s' required pattern='[0-9]{1,2}'
    title='number of software entries needed in runtime environment' /></td>
</tr>""" % software_entries
    html_form += """
<tr>
    <td>Number of environment entries</td>
    <td>
    <input type='number' name='environment_entries' min=0 max=99
    minlength=1 maxlength=2 value='%s' required pattern='[0-9]{1,2}'
    title='number of environment variables provided by runtime environment' />
    </td>
</tr>""" % environment_entries
    output_objects.append({'object_type': 'html_form', 'text': html_form})
    if testprocedure_entry == 0:
        select_string = """<option value='0' selected>No</option>
<option value=1>Yes</option>"""
    elif testprocedure_entry == 1:
        select_string = """<option value='0'>No</option>
<option value='1' selected>Yes</option>"""
    else:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'testprocedure_entry should be 0 or 1, you specified %s' %
            testprocedure_entry
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    html_form = """
<tr>
    <td>Runtime environment has a testprocedure</td>
    <td><select name='testprocedure_entry'>%s</select></td>
</tr>
<tr>
    <td colspan=2>
    <input type='hidden' name='re_template' value='%s' />
    <input type='submit' value='Update fields' />
    </td>
</tr>
</table>
</form><br />
""" % (select_string, re_template)

    form_method = 'post'
    csrf_limit = get_csrf_limit(configuration)
    fill_helpers = {
        'short_title': configuration.short_title,
        'form_method': form_method,
        'csrf_field': csrf_field,
        'csrf_limit': csrf_limit
    }
    target_op = 'createre'
    csrf_token = make_csrf_token(configuration, form_method, target_op,
                                 client_id, csrf_limit)
    fill_helpers.update({'target_op': target_op, 'csrf_token': csrf_token})
    html_form += """
<form method='%(form_method)s' action='%(target_op)s.py'>
<input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
<b>Runtime Environment Name</b><br />
<small>(eg. BASH-2.X-1, must be unique):</small><br />
<input class='p80width' type='text' name='re_name' required 
    pattern='[a-zA-Z0-9_.-]+'
    title='unique name of ASCII letters and digits separated only by underscores, periods and hyphens' />
<br />
<br /><b>Description:</b><br />
<textarea class='p80width' rows='4' name='redescription'>
"""
    if template:
        html_form += template['DESCRIPTION'].replace('<br />', '\n')
    html_form += '</textarea><br />'

    soft_list = []
    if software_entries > 0:
        html_form += '<br /><b>Needed Software:</b><br />'
    if template:
        if 'SOFTWARE' in template:
            soft_list = template['SOFTWARE']
            for soft in soft_list:
                html_form += """
<textarea class='p80width' rows='6' name='software'>"""
                for keyname in soft.keys():
                    if keyname != '':
                        html_form += '%s=%s\n' % (keyname, soft[keyname])
                html_form += '</textarea><br />'

    # loop and create textareas for any missing software entries

    software = rekeywords_dict['SOFTWARE']
    sublevel_required = []
    sublevel_optional = []

    if 'Sublevel' in software and software['Sublevel']:
        sublevel_required = software['Sublevel_required']
        sublevel_optional = software['Sublevel_optional']

    for _ in range(len(soft_list), software_entries):
        html_form += """
<textarea class='p80width' rows='6' name='software'>"""
        for sub_req in sublevel_required:
            html_form += '%s=   # required\n' % sub_req
        for sub_opt in sublevel_optional:
            html_form += '%s=   # optional\n' % sub_opt
        html_form += '</textarea><br />'

    if template and testprocedure_entry == 1:
        if 'TESTPROCEDURE' in template:
            html_form += """
<br /><b>Testprocedure</b> (in mRSL format):<br />
<textarea class='p80width' rows='15' name='testprocedure'>"""

            base64string = ''
            for stringpart in template['TESTPROCEDURE']:
                base64string += stringpart
                decodedstring = base64.decodestring(base64string)
                html_form += decodedstring
            html_form += '</textarea>'
            output_objects.append({
                'object_type': 'html_form',
                'text': html_form
            })

            html_form = """
<br /><b>Expected .stdout file if testprocedure is executed</b><br />
<textarea class='p80width' rows='10' name='verifystdout'>"""

            if 'VERIFYSTDOUT' in template:
                for line in template['VERIFYSTDOUT']:
                    html_form += line
            html_form += '</textarea>'

            html_form += """
<br /><b>Expected .stderr file if testprocedure is executed</b><br />
<textarea cols='50' rows='10' name='verifystderr'>"""
            if 'VERIFYSTDERR' in template:
                for line in template['VERIFYSTDERR']:
                    html_form += line
            html_form += '</textarea>'

            html_form += """
<br /><b>Expected .status file if testprocedure is executed</b><br />
<textarea cols='50' rows='10' name='verifystatus'>"""
            if 'VERIFYSTATUS' in template:
                for line in template['VERIFYSTATUS']:
                    html_form += line
            html_form += '</textarea>'
    elif testprocedure_entry == 1:

        html_form += """
<br /><b>Testprocedure</b> (in mRSL format):<br />
<textarea class='p80width' rows='15' name='testprocedure'>"""

        html_form += \
            """::EXECUTE::
ls    
</textarea>
<br /><b>Expected .stdout file if testprocedure is executed</b><br />
<textarea class='p80width' rows='10' name='verifystdout'></textarea>
<br /><b>Expected .stderr file if testprocedure is executed</b><br />
<textarea class='p80width' rows='10' name='verifystderr'></textarea>
<br /><b>Expected .status file if testprocedure is executed</b><br />
<textarea class='p80width' rows='10' name='verifystatus'></textarea>
"""

    environmentvariable = rekeywords_dict['ENVIRONMENTVARIABLE']
    sublevel_required = []
    sublevel_optional = []

    if 'Sublevel' in environmentvariable\
         and environmentvariable['Sublevel']:
        sublevel_required = environmentvariable['Sublevel_required']
        sublevel_optional = environmentvariable['Sublevel_optional']

    env_list = []
    if environment_entries > 0:
        html_form += '<br /><b>Environments:</b><br />'
    if template:
        if 'ENVIRONMENTVARIABLE' in template:
            env_list = template['ENVIRONMENTVARIABLE']
            for env in env_list:
                html_form += """
<textarea class='p80width' rows='4' name='environment'>"""
                for keyname in env.keys():
                    if keyname != '':
                        html_form += '%s=%s\n' % (keyname, env[keyname])
                html_form += '</textarea><br />'

    # loop and create textareas for any missing environment entries

    for _ in range(len(env_list), environment_entries):
        html_form += """
<textarea class='p80width' rows='4' name='environment'>"""
        for sub_req in sublevel_required:
            html_form += '%s=   # required\n' % sub_req
        for sub_opt in sublevel_optional:
            html_form += '%s=   # optional\n' % sub_opt
        html_form += '</textarea><br />'

    html_form += """<br /><br /><input type='submit' value='Create' />
    </form>
"""
    output_objects.append({
        'object_type': 'html_form',
        'text': html_form % fill_helpers
    })
    return (output_objects, returnvalues.OK)
示例#21
0
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'] = '%s send request' % configuration.short_title
    output_objects.append({
        'object_type': 'header',
        'text': '%s send request' % configuration.short_title
    })
    (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)

    target_id = client_id
    vgrid_name = accepted['vgrid_name'][-1].strip()
    visible_user_names = accepted['cert_id']
    visible_res_names = accepted['unique_resource_name']
    request_type = accepted['request_type'][-1].strip().lower()
    request_text = accepted['request_text'][-1].strip()
    protocols = [proto.strip() for proto in accepted['protocol']]
    use_any = False
    if any_protocol in protocols:
        use_any = True
        protocols = configuration.notify_protocols
    protocols = [proto.lower() for proto in protocols]

    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)

    valid_request_types = [
        'resourceowner', 'resourceaccept', 'resourcereject', 'vgridowner',
        'vgridmember', 'vgridresource', 'vgridaccept', 'vgridreject', 'plain'
    ]
    if not request_type in valid_request_types:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '%s is not a valid request_type (valid types: %s)!' %
            (request_type.lower(), valid_request_types)
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    if not protocols:
        output_objects.append({
            'object_type': 'error_text',
            'text': 'No protocol specified!'
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    user_map = get_user_map(configuration)
    reply_to = user_map[client_id][USERID]
    # Try to point replies to client_id email
    client_email = extract_field(reply_to, 'email')

    if request_type == "plain":
        if not visible_user_names:
            output_objects.append({
                'object_type': 'error_text',
                'text': 'No user ID specified!'
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

        user_id = visible_user_names[-1].strip()
        anon_map = anon_to_real_user_map(configuration)
        if user_id in anon_map:
            user_id = anon_map[user_id]
        if user_id not in user_map:
            output_objects.append({
                'object_type': 'error_text',
                'text': 'No such user: %s' % user_id
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
        target_name = user_id
        user_dict = user_map[user_id]
        vgrid_access = user_vgrid_access(configuration, client_id)
        vgrids_allow_email = user_dict[CONF].get('VGRIDS_ALLOW_EMAIL', [])
        vgrids_allow_im = user_dict[CONF].get('VGRIDS_ALLOW_IM', [])
        if any_vgrid in vgrids_allow_email:
            email_vgrids = vgrid_access
        else:
            email_vgrids = set(vgrids_allow_email).intersection(vgrid_access)
        if any_vgrid in vgrids_allow_im:
            im_vgrids = vgrid_access
        else:
            im_vgrids = set(vgrids_allow_im).intersection(vgrid_access)
        if use_any:
            # Do not try disabled protocols if ANY was requested
            if not email_vgrids:
                protocols = [
                    proto for proto in protocols
                    if proto not in email_keyword_list
                ]
            if not im_vgrids:
                protocols = [
                    proto for proto in protocols if proto in email_keyword_list
                ]
        if not email_vgrids and [
                proto for proto in protocols if proto in email_keyword_list
        ]:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'You are not allowed to send emails to %s!' % user_id
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
        if not im_vgrids and [
                proto for proto in protocols if proto not in email_keyword_list
        ]:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'You are not allowed to send instant messages to %s!' % user_id
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
        for proto in protocols:
            if not user_dict[CONF].get(proto.upper(), False):
                if use_any:
                    # Remove missing protocols if ANY protocol was requested
                    protocols = [i for i in protocols if i != proto]
                else:
                    output_objects.append({
                        'object_type':
                        'error_text',
                        'text':
                        'User %s does not accept %s messages!' %
                        (user_id, proto)
                    })
                    return (output_objects, returnvalues.CLIENT_ERROR)
        if not protocols:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'User %s does not accept requested protocol(s) messages!' %
                user_id
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
        target_list = [user_id]
    elif request_type in ["vgridaccept", "vgridreject"]:
        # Always allow accept messages but only between owners/members
        if not visible_user_names and not visible_res_names:
            output_objects.append({
                'object_type': 'error_text',
                'text': 'No user or resource ID specified!'
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
        if not vgrid_name:
            output_objects.append({
                'object_type': 'error_text',
                'text': 'No vgrid_name specified!'
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
        if vgrid_name.upper() == default_vgrid.upper():
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'No requests for %s are allowed!' % default_vgrid
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
        if not vgrid_is_owner(vgrid_name, client_id, configuration):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'You are not an owner of %s or a parent %s!' %
                (vgrid_name, label)
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
        # NOTE: we support exactly one vgrid but multiple users/resources here
        if visible_user_names:
            logger.info("setting user recipients: %s" % visible_user_names)
            target_list = [user_id.strip() for user_id in visible_user_names]
        elif visible_res_names:
            # vgrid resource accept - lookup and notify resource owners
            logger.info("setting res owner recipients: %s" % visible_res_names)
            target_list = []
            for unique_resource_name in visible_res_names:
                logger.info("loading res owners for %s" % unique_resource_name)
                (load_status,
                 res_owners) = resource_owners(configuration,
                                               unique_resource_name)
                if not load_status:
                    output_objects.append({
                        'object_type':
                        'error_text',
                        'text':
                        'Could not lookup owners of %s!' % unique_resource_name
                    })
                    continue
                logger.info("adding res owners to recipients: %s" % res_owners)
                target_list += [user_id for user_id in res_owners]

        target_id = '%s %s owners' % (vgrid_name, label)
        target_name = vgrid_name
    elif request_type in ["resourceaccept", "resourcereject"]:
        # Always allow accept messages between actual resource owners
        if not visible_user_names:
            output_objects.append({
                'object_type': 'error_text',
                'text': 'No user ID specified!'
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
        if not visible_res_names:
            output_objects.append({
                'object_type': 'error_text',
                'text': 'No resource ID specified!'
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
        # NOTE: we support exactly one resource but multiple users here
        unique_resource_name = visible_res_names[-1].strip()
        target_name = unique_resource_name
        res_map = get_resource_map(configuration)
        if unique_resource_name not in res_map:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'No such resource: %s' % unique_resource_name
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
        owners_list = res_map[unique_resource_name][OWNERS]
        if not client_id in owners_list:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'You are not an owner of %s!' % unique_resource_name
            })
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Invalid resource %s message!' % request_type
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
        target_id = '%s resource owners' % unique_resource_name
        target_name = unique_resource_name
        target_list = [user_id.strip() for user_id in visible_user_names]
    elif request_type == "resourceowner":
        if not visible_res_names:
            output_objects.append({
                'object_type': 'error_text',
                'text': 'No resource ID specified!'
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
        # NOTE: we support exactly one resource but multiple users here
        unique_resource_name = visible_res_names[-1].strip()
        anon_map = anon_to_real_res_map(configuration.resource_home)
        if unique_resource_name in anon_map:
            unique_resource_name = anon_map[unique_resource_name]
        target_name = unique_resource_name
        res_map = get_resource_map(configuration)
        if unique_resource_name not in res_map:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'No such resource: %s' % unique_resource_name
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
        target_list = res_map[unique_resource_name][OWNERS]
        if client_id in target_list:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'You are already an owner of %s!' % unique_resource_name
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

        request_dir = os.path.join(configuration.resource_home,
                                   unique_resource_name)
        access_request = {
            'request_type': request_type,
            'entity': client_id,
            'target': unique_resource_name,
            'request_text': request_text
        }
        if not save_access_request(configuration, request_dir, access_request):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Could not save request - owners may still manually add you'
            })
            return (output_objects, returnvalues.SYSTEM_ERROR)
    elif request_type in ["vgridmember", "vgridowner", "vgridresource"]:
        if not vgrid_name:
            output_objects.append({
                'object_type': 'error_text',
                'text': 'No vgrid_name specified!'
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

        # default vgrid is read-only

        if vgrid_name.upper() == default_vgrid.upper():
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'No requests for %s are not allowed!' % default_vgrid
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

        # stop owner or member request if already an owner
        # and prevent repeated resource access requests

        if request_type == 'vgridresource':
            # NOTE: we support exactly one resource here
            unique_resource_name = visible_res_names[-1].strip()
            target_id = entity = unique_resource_name
            if vgrid_is_resource(vgrid_name, unique_resource_name,
                                 configuration):
                output_objects.append({
                    'object_type':
                    'error_text',
                    'text':
                    'You already have access to %s or a parent %s.' %
                    (vgrid_name, label)
                })
                return (output_objects, returnvalues.CLIENT_ERROR)
        else:
            target_id = entity = client_id
            if vgrid_is_owner(vgrid_name, client_id, configuration):
                output_objects.append({
                    'object_type':
                    'error_text',
                    'text':
                    'You are already an owner of %s or a parent %s!' %
                    (vgrid_name, label)
                })
                return (output_objects, returnvalues.CLIENT_ERROR)

        # only ownership requests are allowed for existing members

        if request_type == 'vgridmember':
            if vgrid_is_member(vgrid_name, client_id, configuration):
                output_objects.append({
                    'object_type':
                    'error_text',
                    'text':
                    'You are already a member of %s or a parent %s.' %
                    (vgrid_name, label)
                })
                return (output_objects, returnvalues.CLIENT_ERROR)

        # Find all VGrid owners configured to receive notifications

        target_name = vgrid_name
        (settings_status, settings_dict) = vgrid_settings(vgrid_name,
                                                          configuration,
                                                          recursive=True,
                                                          as_dict=True)
        if not settings_status:
            settings_dict = {}
        request_recipients = settings_dict.get('request_recipients',
                                               default_vgrid_settings_limit)
        # We load and use direct owners first if any - otherwise inherited
        owners_list = []
        for inherited in (False, True):
            (owners_status, owners_list) = vgrid_owners(vgrid_name,
                                                        configuration,
                                                        recursive=inherited)
            if not owners_status:
                output_objects.append({
                    'object_type':
                    'error_text',
                    'text':
                    'Failed to lookup owners for %s %s - sure it exists?' %
                    (vgrid_name, label)
                })
                return (output_objects, returnvalues.CLIENT_ERROR)
            elif owners_list:
                break
        if not owners_list:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Failed to lookup owners for %s %s - sure it exists?' %
                (vgrid_name, label)
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
        # Now we have direct or inherited owners to notify
        target_list = owners_list[:request_recipients]

        request_dir = os.path.join(configuration.vgrid_home, vgrid_name)
        access_request = {
            'request_type': request_type,
            'entity': entity,
            'target': vgrid_name,
            'request_text': request_text
        }
        if not save_access_request(configuration, request_dir, access_request):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Could not save request - owners may still manually add you'
            })
            return (output_objects, returnvalues.SYSTEM_ERROR)

    else:
        output_objects.append({
            'object_type': 'error_text',
            'text': 'Invalid request type: %s' % request_type
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Now send request to all targets in turn
    # TODO: inform requestor if no owners have mail/IM set in their settings

    logger.debug("sending notification to recipients: %s" % target_list)

    for target in target_list:

        if not target:
            logger.warning("skipping empty notify target: %s" % target_list)
            continue

        # USER_CERT entry is destination

        notify = []
        for proto in protocols:
            notify.append('%s: SETTINGS' % proto)
        job_dict = {
            'NOTIFY': notify,
            'JOB_ID': 'NOJOBID',
            'USER_CERT': target,
            'EMAIL_SENDER': client_email
        }

        notifier = notify_user_thread(
            job_dict,
            [target_id, target_name, request_type, request_text, reply_to],
            'SENDREQUEST',
            logger,
            '',
            configuration,
        )

        # Try finishing delivery but do not block forever on one message
        notifier.join(30)
    output_objects.append({
        'object_type':
        'text',
        'text':
        'Sent %s message to %d people' % (request_type, len(target_list))
    })
    im_notify_protocols = [
        i for i in configuration.notify_protocols if i != 'email'
    ]
    if im_notify_protocols:
        enabled_notify = 'IM / email'
    else:
        enabled_notify = 'email'
    output_objects.append({
        'object_type':
        'text',
        'text':
        """Please make sure you have %s notifications
configured on your Setings page if you expect a reply to this message""" %
        enabled_notify
    })

    return (output_objects, returnvalues.OK)
示例#22
0
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)

    logger.debug("User: %s executing %s", client_id, op_name)
    if not configuration.site_enable_jupyter:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'The Jupyter service is not enabled on the system'
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    if not configuration.site_enable_sftp_subsys and not \
            configuration.site_enable_sftp:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'The required sftp service is not enabled on the system'
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    services = [{
        'object_type': 'service',
        'name': options['service_name'],
        'description': options.get('service_desc', '')
    } for options in configuration.jupyter_services]

    # Show jupyter services menu
    (add_import, add_init, add_ready) = man_base_js(configuration, [])

    add_ready += '''
        /* NOTE: requires managers CSS fix for proper tab bar height */
        $(".jupyter-tabs").tabs();
    '''

    title_entry = find_entry(output_objects, 'title')
    title_entry['script']['advanced'] += add_import
    title_entry['script']['init'] += add_init
    title_entry['script']['ready'] += add_ready

    output_objects.append({
        'object_type': 'header',
        'text': 'Select a Jupyter Service'
    })

    fill_helpers = {
        'jupyter_tabs':
        ''.join([
            '<li><a href="#%s-tab">%s</a></li>' %
            (service['name'], service['name']) for service in services
        ])
    }

    output_objects.append({
        'object_type':
        'html_form',
        'text':
        '''
    <div id="wrap-tabs" class="jupyter-tabs">
    <ul>
    %(jupyter_tabs)s
    </ul>
    ''' % fill_helpers
    })

    for service in services:
        output_objects.append({
            'object_type':
            'html_form',
            'text':
            '''
        <div id="%s-tab">
        ''' % (service['name'])
        })

        if service['description']:
            output_objects.append({
                'object_type': 'sectionheader',
                'text': 'Service Description'
            })
        output_objects.append({
            'object_type':
            'html_form',
            'text':
            '''
        <div class="jupyter-description">
        <p>%s</p>
        </div>
        ''' % service['description']
        })
        output_objects.append({
            'object_type': 'html_form',
            'text': '''
        <br/>
        '''
        })

        output_service = {
            'object_type': 'service',
            'name': "Start %s" % service['name'],
            'targetlink': 'reqjupyterservice.py?service=%s' % service['name']
        }
        output_objects.append(output_service)
        output_objects.append({
            'object_type': 'html_form',
            'text': '''
        </div>
        '''
        })
    output_objects.append({
        'object_type': 'html_form',
        'text': '''
    </div>
    </div>
    '''
    })

    return (output_objects, returnvalues.OK)
示例#23
0
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'] = "Reject %s Request" % label
    output_objects.append({
        'object_type': 'header',
        'text': 'Reject %s Request' % label
    })
    (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)

    vgrid_name = accepted['vgrid_name'][-1].strip()
    request_name = unhexlify(accepted['request_name'][-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)

    # Validity of user and vgrid names is checked in this init function so
    # no need to worry about illegal directory traversal through variables

    (ret_val, msg, ret_variables) = \
        init_vgrid_script_add_rem(vgrid_name, client_id, request_name,
                                  'request', configuration)
    if not ret_val:
        output_objects.append({'object_type': 'error_text', 'text': msg})
        return (output_objects, returnvalues.CLIENT_ERROR)
    elif msg:

        # In case of warnings, msg is non-empty while ret_val remains True

        output_objects.append({'object_type': 'warning', 'text': msg})

    if request_name:
        request_dir = os.path.join(configuration.vgrid_home, vgrid_name)
        req = load_access_request(configuration, request_dir, request_name)
    if not req or not delete_access_request(configuration, request_dir,
                                            request_name):
        logger.error("failed to delete owner request for %s in %s" % \
                     (vgrid_name, request_name))
        output_objects.append({
            'object_type': 'error_text', 'text':
            'Failed to remove saved vgrid request for %s in %s!'\
            % (vgrid_name, request_name)})
        return (output_objects, returnvalues.CLIENT_ERROR)
    output_objects.append({
        'object_type':
        'text',
        'text':
        '''
Deleted %(request_type)s access request to %(target)s for %(entity)s .
''' % req
    })
    if req['request_type'] == 'vgridresource':
        id_field = "unique_resource_name"
    else:
        id_field = "cert_id"
    form_method = 'post'
    csrf_limit = get_csrf_limit(configuration)
    fill_helpers = {
        'protocol': any_protocol,
        'id_field': id_field,
        'vgrid_label': label,
        'form_method': form_method,
        'csrf_field': csrf_field,
        'csrf_limit': csrf_limit
    }
    fill_helpers.update(req)
    target_op = 'sendrequestaction'
    csrf_token = make_csrf_token(configuration, form_method, target_op,
                                 client_id, csrf_limit)
    fill_helpers.update({'target_op': target_op, 'csrf_token': csrf_token})

    output_objects.append({
        'object_type':
        'html_form',
        'text':
        """
<p>
You can use the reply form below if you want to additionally send an
explanation for rejecting the request.
</p>
<form method='%(form_method)s' action='%(target_op)s.py'>
<input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
<input type=hidden name=request_type value='vgridreject' />
<input type=hidden name=vgrid_name value='%(target)s' />
<input type=hidden name=%(id_field)s value='%(entity)s' />
<input type=hidden name=protocol value='%(protocol)s' />
<table>
<tr>
<td class='title'>Optional reject message to requestor(s)</td>
</tr><tr>
<td><textarea name=request_text cols=72 rows=10>
We have decided to reject your %(request_type)s request to our %(target)s
%(vgrid_label)s.

Regards, the %(target)s %(vgrid_label)s owners
</textarea></td>
</tr>
<tr>
<td><input type='submit' value='Inform requestor(s)' /></td>
</tr>
</table>
</form>
<br />
""" % fill_helpers
    })
    output_objects.append({
        'object_type': 'link',
        'destination': 'adminvgrid.py?vgrid_name=%s' % vgrid_name,
        'text': 'Back to administration for %s' % vgrid_name
    })
    return (output_objects, returnvalues.OK)
示例#24
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id)
    client_dir = client_id_dir(client_id)
    defaults = signature()[1]
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
        # NOTE: path can use wildcards, dst and current_dir cannot
        typecheck_overrides={'path': valid_path_pattern},
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    flags = ''.join(accepted['flags'])
    algo_list = accepted['hash_algo']
    max_chunks = int(accepted['max_chunks'][-1])
    pattern_list = accepted['path']
    dst = accepted['dst'][-1]
    current_dir = accepted['current_dir'][-1].lstrip(os.sep)

    # All paths are relative to current_dir

    pattern_list = [os.path.join(current_dir, i) for i in pattern_list]
    if dst:
        dst = os.path.join(current_dir, dst)

    # 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.user_home, client_dir)) + os.sep

    status = returnvalues.OK

    if verbose(flags):
        for flag in flags:
            output_objects.append({
                'object_type': 'text',
                'text': '%s using flag: %s' % (op_name, flag)
            })

    # IMPORTANT: path must be expanded to abs for proper chrooting
    abs_dir = os.path.abspath(
        os.path.join(base_dir, current_dir.lstrip(os.sep)))
    if not valid_user_path(configuration, abs_dir, base_dir, True):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            "You're not allowed to work in %s!" % current_dir
        })
        logger.warning('%s tried to %s restricted path %s ! (%s)' %
                       (client_id, op_name, abs_dir, current_dir))
        return (output_objects, returnvalues.CLIENT_ERROR)

    if verbose(flags):
        output_objects.append({
            'object_type': 'text',
            'text': "working in %s" % current_dir
        })

    if dst:
        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)

        # NOTE: dst already incorporates current_dir prefix here
        # IMPORTANT: path must be expanded to abs for proper chrooting
        abs_dest = os.path.abspath(os.path.join(base_dir, dst))
        logger.info('chksum in %s' % abs_dest)

        # Don't use abs_path in output as it may expose underlying
        # fs layout.

        relative_dest = abs_dest.replace(base_dir, '')
        if not valid_user_path(configuration, abs_dest, base_dir, True):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "Invalid path! (%s expands to an illegal path)" % dst
            })
            logger.warning('%s tried to %s restricted path %s !(%s)' %
                           (client_id, op_name, abs_dest, dst))
            return (output_objects, returnvalues.CLIENT_ERROR)
        if not check_write_access(abs_dest, parent_dir=True):
            logger.warning('%s called without write access: %s' %
                           (op_name, abs_dest))
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'cannot checksum to "%s": inside a read-only location!' %
                relative_dest
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

    all_lines = []
    for pattern in pattern_list:

        # Check directory traversal attempts before actual handling to avoid
        # leaking information about file system layout while allowing
        # consistent error messages

        unfiltered_match = glob.glob(base_dir + pattern)
        match = []
        for server_path in unfiltered_match:
            # IMPORTANT: path must be expanded to abs for proper chrooting
            abs_path = os.path.abspath(server_path)
            if not valid_user_path(configuration, abs_path, base_dir, True):

                # out of bounds - save user warning for later to allow
                # partial match:
                # ../*/* is technically allowed to match own files.

                logger.warning('%s tried to %s restricted path %s ! (%s)' %
                               (client_id, op_name, abs_path, pattern))
                continue
            match.append(abs_path)

        # Now actually treat list of allowed matchings and notify if no
        # (allowed) match

        if not match:
            output_objects.append({
                'object_type': 'file_not_found',
                'name': pattern
            })
            status = returnvalues.FILE_NOT_FOUND

        for abs_path in match:
            relative_path = abs_path.replace(base_dir, '')
            output_lines = []
            for hash_algo in algo_list:
                try:
                    chksum_helper = _algo_map.get(hash_algo, _algo_map["md5"])
                    checksum = chksum_helper(abs_path, max_chunks=max_chunks)
                    line = "%s %s\n" % (checksum, relative_path)
                    logger.info("%s %s of %s: %s" %
                                (op_name, hash_algo, abs_path, checksum))
                    output_lines.append(line)
                except Exception as exc:
                    output_objects.append({
                        'object_type':
                        'error_text',
                        'text':
                        "%s: '%s': %s" % (op_name, relative_path, exc)
                    })
                    logger.error("%s: failed on '%s': %s" %
                                 (op_name, relative_path, exc))
                    status = returnvalues.SYSTEM_ERROR
                    continue
            entry = {'object_type': 'file_output', 'lines': output_lines}
            output_objects.append(entry)
            all_lines += output_lines

    if dst and not write_file(''.join(all_lines), abs_dest, logger):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            "failed to write checksums to %s" % relative_dest
        })
        logger.error("writing checksums to %s for %s failed" %
                     (abs_dest, client_id))
        status = returnvalues.SYSTEM_ERROR

    return (output_objects, status)
示例#25
0
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)
    client_dir = client_id_dir(client_id)
    defaults = signature()[1]
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
        # NOTE: path cannot use wildcards here
        typecheck_overrides={},
    )

    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    status = returnvalues.OK

    chroot = ''
    if configuration.site_enable_gdp:
        chroot = get_project_from_client_id(configuration, client_id)

    all_paths = accepted['path']
    entry_path = all_paths[-1]
    title_entry = find_entry(output_objects, 'title')
    user_settings = title_entry.get('user_settings', {})
    title_entry['text'] = 'File Manager'
    title_entry['style'] = css_tmpl(configuration, user_settings)

    legacy_buttons = False
    if legacy_user_interface(configuration, user_settings):
        legacy_buttons = True
        logger.debug("enable legacy buttons")

    if configuration.site_enable_jobs and \
            'submitjob' in extract_menu(configuration, title_entry):
        enable_submit = 'true'
    else:
        enable_submit = 'false'
    csrf_map = {}
    method = 'post'
    limit = get_csrf_limit(configuration)
    for target_op in csrf_backends:
        csrf_map[target_op] = make_csrf_token(configuration, method, target_op,
                                              client_id, limit)
    (add_import, add_init,
     add_ready) = js_tmpl_parts(configuration, entry_path, enable_submit,
                                str(configuration.site_enable_preview),
                                legacy_buttons, csrf_map, chroot)
    title_entry['script']['advanced'] += add_import
    title_entry['script']['init'] += add_init
    title_entry['script']['ready'] += add_ready
    title_entry['container_class'] = 'fillwidth',

    output_objects.append({
        'object_type': 'header',
        'class': 'fileman-title',
        'text': 'File Manager'
    })
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        html_tmpl(configuration, client_id, title_entry, csrf_map, chroot)
    })

    if len(all_paths) > 1:
        output_objects.append({
            'object_type': 'sectionheader',
            'text': 'All requested paths:'
        })
        for path in all_paths:
            output_objects.append({
                'object_type': 'link',
                'text': path,
                'destination': 'fileman.py?path=%s' % path
            })
            output_objects.append({'object_type': 'text', 'text': ''})

    return (output_objects, status)
示例#26
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id)
    client_dir = client_id_dir(client_id)
    status = returnvalues.OK
    defaults = signature()[1]
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
        # NOTE: path can use wildcards
        typecheck_overrides={'path': valid_path_pattern},
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    flags = ''.join(accepted['flags'])
    patterns = accepted['path']
    search = accepted['pattern'][-1]

    # 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.user_home,
                                            client_dir)) + os.sep

    if verbose(flags):
        for flag in flags:
            output_objects.append({'object_type': 'text', 'text':
                                   '%s using flag: %s' % (op_name, flag)})

    for pattern in patterns:

        # Check directory traversal attempts before actual handling to avoid
        # leaking information about file system layout while allowing
        # consistent error messages

        unfiltered_match = glob.glob(base_dir + pattern)
        match = []
        for server_path in unfiltered_match:
            # IMPORTANT: path must be expanded to abs for proper chrooting
            abs_path = os.path.abspath(server_path)
            if not valid_user_path(configuration, abs_path, base_dir, True):

                # out of bounds - save user warning for later to allow
                # partial match:
                # ../*/* is technically allowed to match own files.

                logger.warning('%s tried to %s restricted path %s! (%s)'
                               % (client_id, op_name, abs_path, pattern))
                continue
            match.append(abs_path)

        # Now actually treat list of allowed matchings and notify if no
        # (allowed) match

        if not match:
            output_objects.append({'object_type': 'file_not_found',
                                   'name': pattern})
            status = returnvalues.FILE_NOT_FOUND

        for abs_path in match:
            relative_path = abs_path.replace(base_dir, '')
            output_lines = []
            try:
                matching = pattern_match_file(search, abs_path)
                for line in matching:
                    output_lines.append(line)
            except Exception as exc:
                output_objects.append({'object_type': 'error_text',
                                       'text': "%s: '%s': %s" % (op_name,
                                                                 relative_path, exc)})
                logger.error("%s: failed on '%s': %s" % (op_name,
                                                         relative_path, exc))
                status = returnvalues.SYSTEM_ERROR
                continue
            entry = {'object_type': 'file_output',
                     'lines': output_lines,
                     'wrap_binary': binary(flags),
                     'wrap_targets': ['lines']}
            if verbose(flags):
                entry['path'] = relative_path
            output_objects.append(entry)

    return (output_objects, status)
示例#27
0
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'] = "Administrate %s" % label
    # NOTE: Delay header entry here to include vgrid name
    (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)

    vgrid_name = accepted['vgrid_name'][-1]

    # prepare for confirm dialog, tablesort and toggling the views (css/js)

    # jquery support for tablesorter and confirmation on request and leave
    # requests table initially sorted by 0, 4, 3 (type first, then date and
    # with alphabetical client ID last)
    # sharelinks table initially sorted by 5, 4 reversed (active first and
    # in growing age)

    table_specs = [{
        'table_id': 'accessrequeststable',
        'pager_id': 'accessrequests_pager',
        'sort_order': '[[0,0],[4,0],[3,0]]'
    }, {
        'table_id': 'sharelinkstable',
        'pager_id': 'sharelinks_pager',
        'sort_order': '[[5,1],[4,1]]'
    }]
    (add_import, add_init, add_ready) = man_base_js(configuration, table_specs,
                                                    {'width': 600})
    add_init += '''
        var toggleHidden = function(classname) {
            // classname supposed to have a leading dot
            $(classname).toggleClass("hidden");
        };
        /* helpers for dynamic form input fields */
        function onOwnerInputChange() {
            makeSpareFields("#dynownerspares", "cert_id");
        }
        function onMemberInputChange() {
            makeSpareFields("#dynmemberspares", "cert_id");
        }
        function onResourceInputChange() {
            makeSpareFields("#dynresourcespares", "unique_resource_name");
        }
    '''
    add_ready += '''
    /* init add owners/member/resource forms with dynamic input fields */
    onOwnerInputChange();
    $("#dynownerspares").on("blur", "input[name=cert_id]",
        function(event) {
            //console.debug("in add owner blur handler");
            onOwnerInputChange();
        }
    );
    onMemberInputChange();
    $("#dynmemberspares").on("blur", "input[name=cert_id]",
        function(event) {
            //console.debug("in add member blur handler");
            onMemberInputChange();
        }
    );'''
    if configuration.site_enable_resources:
        add_ready += '''
    onResourceInputChange();
    $("#dynresourcespares").on("blur", "input[name=unique_resource_name]",
        function(event) {
            console.debug("in resource blur handler");
            onResourceInputChange();
        }
    );
    '''
    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)
    })

    form_method = 'post'
    csrf_limit = get_csrf_limit(configuration)
    fill_helpers = {
        'short_title': configuration.short_title,
        'vgrid_label': label,
        'form_method': form_method,
        'csrf_field': csrf_field,
        'csrf_limit': csrf_limit
    }

    output_objects.append({
        'object_type': 'header',
        'text': "Administrate '%s'" % vgrid_name
    })

    if not vgrid_is_owner(vgrid_name, client_id, configuration):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Only owners of %s can administrate it.' % vgrid_name
        })
        target_op = "sendrequestaction"
        csrf_token = make_csrf_token(configuration, form_method, target_op,
                                     client_id, csrf_limit)
        js_name = 'reqvgridowner%s' % hexlify(vgrid_name)
        helper = html_post_helper(
            js_name, '%s.py' % target_op, {
                'vgrid_name': vgrid_name,
                'request_type': 'vgridowner',
                'request_text': '',
                csrf_field: csrf_token
            })
        output_objects.append({'object_type': 'html_form', 'text': helper})
        output_objects.append({
            'object_type':
            'link',
            'destination':
            "javascript: confirmDialog(%s, '%s', '%s');" %
            (js_name, "Request ownership of " + vgrid_name + ":<br/>" +
             "\nPlease write a message to the owners below.", 'request_text'),
            'class':
            'addadminlink iconspace',
            'title':
            'Request ownership of %s' % vgrid_name,
            'text':
            'Apply to become an owner'
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    for (item, scr) in zip(['owner', 'member', 'resource'],
                           ['vgridowner', 'vgridmember', 'vgridres']):
        if item == 'resource' and not configuration.site_enable_resources:
            continue
        output_objects.append({
            'object_type': 'sectionheader',
            'text': "%ss" % item.title()
        })
        (init_status, oobjs) = vgrid_add_remove_table(client_id, vgrid_name,
                                                      item, scr, configuration)
        if not init_status:
            output_objects.extend(oobjs)
            return (output_objects, returnvalues.SYSTEM_ERROR)
        else:
            output_objects.append({
                'object_type': 'html_form',
                'text': '<div class="div-%s">' % item
            })
            output_objects.append({
                'object_type':
                'link',
                'destination':
                "javascript:toggleHidden('.div-%s');" % item,
                'class':
                'removeitemlink iconspace',
                'title':
                'Toggle view',
                'text':
                'Hide %ss' % item.title()
            })
            output_objects.extend(oobjs)
            output_objects.append({
                'object_type':
                'html_form',
                'text':
                '</div><div class="hidden div-%s">' % item
            })
            output_objects.append({
                'object_type':
                'link',
                'destination':
                "javascript:toggleHidden('.div-%s');" % item,
                'class':
                'additemlink iconspace',
                'title':
                'Toggle view',
                'text':
                'Show %ss' % item.title()
            })
            output_objects.append({
                'object_type': 'html_form',
                'text': '</div>'
            })

    # Pending requests

    target_op = "addvgridowner"
    csrf_token = make_csrf_token(configuration, form_method, target_op,
                                 client_id, csrf_limit)
    helper = html_post_helper(
        "acceptvgridownerreq", "%s.py" % target_op, {
            'vgrid_name': vgrid_name,
            'cert_id': '__DYNAMIC__',
            'request_name': '__DYNAMIC__',
            csrf_field: csrf_token
        })
    output_objects.append({'object_type': 'html_form', 'text': helper})
    target_op = "addvgridmember"
    csrf_token = make_csrf_token(configuration, form_method, target_op,
                                 client_id, csrf_limit)
    helper = html_post_helper(
        "acceptvgridmemberreq", "%s.py" % target_op, {
            'vgrid_name': vgrid_name,
            'cert_id': '__DYNAMIC__',
            'request_name': '__DYNAMIC__',
            csrf_field: csrf_token
        })
    output_objects.append({'object_type': 'html_form', 'text': helper})
    target_op = "addvgridres"
    csrf_token = make_csrf_token(configuration, form_method, target_op,
                                 client_id, csrf_limit)
    helper = html_post_helper(
        "acceptvgridresourcereq", "%s.py" % target_op, {
            'vgrid_name': vgrid_name,
            'unique_resource_name': '__DYNAMIC__',
            'request_name': '__DYNAMIC__',
            csrf_field: csrf_token
        })
    output_objects.append({'object_type': 'html_form', 'text': helper})
    target_op = "rejectvgridreq"
    csrf_token = make_csrf_token(configuration, form_method, target_op,
                                 client_id, csrf_limit)
    helper = html_post_helper(
        "rejectvgridreq", "%s.py" % target_op, {
            'vgrid_name': vgrid_name,
            'request_name': '__DYNAMIC__',
            csrf_field: csrf_token
        })
    output_objects.append({'object_type': 'html_form', 'text': helper})

    request_dir = os.path.join(configuration.vgrid_home, vgrid_name)
    request_list = []
    for req_name in list_access_requests(configuration, request_dir):
        req = load_access_request(configuration, request_dir, req_name)
        if not req:
            continue
        if not req.get('request_type',
                       None) in ["vgridowner", "vgridmember", "vgridresource"]:
            logger.error("unexpected request_type %(request_type)s" % req)
            continue
        request_item = build_accessrequestitem_object(configuration, req)
        # Convert filename with exotic chars into url-friendly pure hex version
        shared_args = {"request_name": hexlify(req["request_name"])}
        accept_args, reject_args = {}, {}
        accept_args.update(shared_args)
        reject_args.update(shared_args)
        if req['request_type'] == "vgridresource":
            accept_args["unique_resource_name"] = req["entity"]
        else:
            accept_args["cert_id"] = req["entity"]

        request_item['acceptrequestlink'] = {
            'object_type':
            'link',
            'destination':
            "javascript: confirmDialog(%s, '%s', %s, %s);" %
            ("accept%(request_type)sreq" % req,
             "Accept %(target)s %(request_type)s request from %(entity)s" %
             req, 'undefined', "{%s}" %
             ', '.join(["'%s': '%s'" % pair for pair in accept_args.items()])),
            'class':
            'addlink iconspace',
            'title':
            'Accept %(target)s %(request_type)s request from %(entity)s' % req,
            'text':
            ''
        }
        request_item['rejectrequestlink'] = {
            'object_type':
            'link',
            'destination':
            "javascript: confirmDialog(%s, '%s', %s, %s);" %
            ("rejectvgridreq",
             "Reject %(target)s %(request_type)s request from %(entity)s" %
             req, 'undefined', "%s" % reject_args),
            'class':
            'removelink iconspace',
            'title':
            'Reject %(target)s %(request_type)s request from %(entity)s' % req,
            'text':
            ''
        }

        request_list.append(request_item)

    output_objects.append({
        'object_type': 'sectionheader',
        'text': "Pending Requests"
    })
    output_objects.append({
        'object_type': 'table_pager',
        'id_prefix': 'accessrequests_',
        'entry_name': 'access requests',
        'default_entries': default_pager_entries
    })
    output_objects.append({
        'object_type': 'accessrequests',
        'accessrequests': request_list
    })

    # VGrid Share links

    # Table columns to skip
    skip_list = [
        'editsharelink', 'delsharelink', 'invites', 'expire', 'single_file'
    ]

    # NOTE: Inheritance is a bit tricky for sharelinks because parent shares
    # only have relevance if they actually share a path that is a prefix of
    # vgrid_name.

    (share_status, share_list) = vgrid_sharelinks(vgrid_name, configuration)
    sharelinks = []
    if share_status:
        for share_dict in share_list:
            rel_path = share_dict['path'].strip(os.sep)
            parent_vgrids = vgrid_list_parents(vgrid_name, configuration)
            include_share = False
            # Direct sharelinks (careful not to greedy match A/B with A/BCD)
            if rel_path == vgrid_name or \
                    rel_path.startswith(vgrid_name+os.sep):
                include_share = True
            # Parent vgrid sharelinks that in effect also give access here
            for parent in parent_vgrids:
                if rel_path == parent:
                    include_share = True
            if include_share:
                share_item = build_sharelinkitem_object(
                    configuration, share_dict)
                sharelinks.append(share_item)
    else:
        logger.warning("failed to load vgrid sharelinks for %s: %s" %
                       (vgrid_name, share_list))

    output_objects.append({
        'object_type': 'sectionheader',
        'text': "Share Links"
    })
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        '<p>Current share links in %s shared folder</p>' % vgrid_name
    })
    output_objects.append({
        'object_type': 'table_pager',
        'id_prefix': 'sharelinks_',
        'entry_name': 'share links',
        'default_entries': default_pager_entries
    })
    output_objects.append({
        'object_type': 'sharelinks',
        'sharelinks': sharelinks,
        'skip_list': skip_list
    })

    # VGrid settings

    output_objects.append({'object_type': 'sectionheader', 'text': "Settings"})

    (direct_status, direct_dict) = vgrid_settings(vgrid_name,
                                                  configuration,
                                                  recursive=False,
                                                  as_dict=True)
    if not direct_status or not direct_dict:
        direct_dict = {}
    (settings_status, settings_dict) = vgrid_settings(vgrid_name,
                                                      configuration,
                                                      recursive=True,
                                                      as_dict=True)
    if not settings_status or not settings_dict:
        settings_dict = {}
    form_method = 'post'
    csrf_limit = get_csrf_limit(configuration)
    # Always set these values
    settings_dict.update({
        'vgrid_name': vgrid_name,
        'vgrid_label': label,
        'owners': keyword_owners,
        'members': keyword_members,
        'all': keyword_all,
        'form_method': form_method,
        'csrf_field': csrf_field,
        'csrf_limit': csrf_limit
    })
    target_op = 'vgridsettings'
    csrf_token = make_csrf_token(configuration, form_method, target_op,
                                 client_id, csrf_limit)
    settings_dict.update({'target_op': target_op, 'csrf_token': csrf_token})

    settings_form = '''
    <form method="%(form_method)s" action="%(target_op)s.py">
        <fieldset>
            <legend>%(vgrid_label)s configuration</legend>
                <input type="hidden" name="%(csrf_field)s" value="%(csrf_token)s" />
                <input type="hidden" name="vgrid_name" value="%(vgrid_name)s" />
'''
    description = settings_dict.get('description', '')
    settings_form += '''
            <h4>Public description</h4>
                <textarea class="fillwidth padspace" name="description" rows=10
                    >%s</textarea>
''' % description
    settings_form += '<br/>'

    settings_form += '''<p>All visibility options below can be set to owners,
members or everyone and by default only owners can see participation. In effect
setting visibility to <em>members</em> means that owners and members can see
the corresponding participants. Similarly setting a visibility flag to
<em>everyone</em> means that all %s users can see the participants.</p>
''' % configuration.short_title
    visibility_options = [("Owners are visible to", "visible_owners"),
                          ("Members are visible to", "visible_members"),
                          ("Resources are visible to", "visible_resources")]
    for (title, field) in visibility_options:
        settings_form += '<h4>%s</h4>' % title
        if direct_dict.get(field, False):
            choices = _valid_visible + _reset_choice
        else:
            choices = _valid_visible + _keep_choice
        for (key, val) in choices:
            checked = ''
            if settings_dict.get(field, keyword_owners) == val:
                checked = "checked"
            settings_form += '''
            <input type="radio" name="%s" value="%s" %s/> %s
''' % (field, val, checked, key)
        settings_form += '<br/>'
    field = 'restrict_settings_adm'
    restrict_settings_adm = settings_dict.get(field,
                                              default_vgrid_settings_limit)
    if direct_dict.get(field, False):
        direct_note = _reset_note
    else:
        direct_note = _keep_note
    settings_form += '''
            <h4>Restrict Settings</h4>
            Restrict changing of these settings to only the first
            <input type="number" name="restrict_settings_adm" min=0 max=999
            minlength=1 maxlength=3 value=%d required />
            owners %s.
''' % (restrict_settings_adm, direct_note)
    settings_form += '<br/>'
    field = 'restrict_owners_adm'
    restrict_owners_adm = settings_dict.get(field,
                                            default_vgrid_settings_limit)
    if direct_dict.get(field, False):
        direct_note = _reset_note
    else:
        direct_note = _keep_note
    settings_form += '''
            <h4>Restrict Owner Administration</h4>
            Restrict administration of owners to only the first
            <input type="number" name="restrict_owners_adm" min=0 max=999
            minlength=1 maxlength=3 value=%d required />
            owners %s.
''' % (restrict_owners_adm, direct_note)
    settings_form += '<br/>'
    field = 'restrict_members_adm'
    restrict_members_adm = settings_dict.get(field,
                                             default_vgrid_settings_limit)
    if direct_dict.get(field, False):
        direct_note = _reset_note
    else:
        direct_note = _keep_note
    settings_form += '''
            <h4>Restrict Member Administration</h4>
            Restrict administration of members to only the first
            <input type="number" name="restrict_members_adm" min=0 max=999
            minlength=1 maxlength=3 value=%d required />
            owners %s.
''' % (restrict_members_adm, direct_note)
    settings_form += '<br/>'
    field = 'restrict_resources_adm'
    restrict_resources_adm = settings_dict.get(field,
                                               default_vgrid_settings_limit)
    if direct_dict.get(field, False):
        direct_note = _reset_note
    else:
        direct_note = _keep_note
    settings_form += '''
            <h4>Restrict Resource Administration</h4>
            Restrict administration of resources to only the first
            <input type="number" name="restrict_resources_adm" min=0 max=999
            minlength=1 maxlength=3 value=%d required />
            owners %s.
''' % (restrict_resources_adm, direct_note)
    settings_form += '<br/>'
    if vgrid_restrict_write_support(configuration):
        settings_form += '''<p>All write access options below can be set to
owners, members or none. By default only owners can write web pages while
owners and members can edit data in the shared folders. In effect setting write
access to <em>members</em> means that owners and members have full access.
Similarly setting a write access flag to <em>owners</em> means that only owners
can modify the data, while members can only read and use it. Finally setting a
write access flag to <em>none</em> means that neither owners nor members can
modify the data there, effectively making it read-only. Some options are not
yet supported and thus are disabled below.
</p>
'''
        writable_options = [
            ("Shared files write access", "write_shared_files",
             keyword_members),
            ("Private web page write access", "write_priv_web",
             keyword_owners),
            ("Public web page write access", "write_pub_web", keyword_owners),
        ]
    else:
        writable_options = []
    for (title, field, default) in writable_options:
        settings_form += '<h4>%s</h4>' % title
        if direct_dict.get(field, False):
            choices = _valid_write_access + _reset_choice
        else:
            choices = _valid_write_access + _keep_choice
        for (key, val) in choices:
            disabled = ''
            # TODO: remove these artifical limits once we support changing
            # TODO: also add check for vgrid web reshare in sharelink then
            if field == 'write_shared_files' and val == keyword_owners:
                disabled = 'disabled'
            elif field == 'write_priv_web' and val in [
                    keyword_members, keyword_none
            ]:
                disabled = 'disabled'
            elif field == 'write_pub_web' and val in [
                    keyword_members, keyword_none
            ]:
                disabled = 'disabled'
            checked = ''
            if settings_dict.get(field, default) == val:
                checked = "checked"
            settings_form += '''
            <input type="radio" name="%s" value="%s" %s %s /> %s
''' % (field, val, checked, disabled, key)
        settings_form += '<br/>'
    sharelink_options = [("Limit sharelink creation to", "create_sharelink")]
    for (title, field) in sharelink_options:
        settings_form += '<h4>%s</h4>' % title
        if direct_dict.get(field, False):
            choices = _valid_sharelink + _reset_choice
        else:
            choices = _valid_sharelink + _keep_choice
        for (key, val) in choices:
            checked = ''
            if settings_dict.get(field, keyword_owners) == val:
                checked = "checked"
            settings_form += '''
            <input type="radio" name="%s" value="%s" %s/> %s
''' % (field, val, checked, key)
        settings_form += '<br/>'
    field = 'request_recipients'
    request_recipients = settings_dict.get(field, default_vgrid_settings_limit)
    if direct_dict.get(field, False):
        direct_note = _reset_note
    else:
        direct_note = _keep_note
    settings_form += '''
            <h4>Request Recipients</h4>
            Notify only first
            <input type="number" name="request_recipients" min=0 max=999
            minlength=1 maxlength=3 value=%d required />
            owners about access requests %s.
''' % (request_recipients, direct_note)
    settings_form += '<br/>'

    bool_options = [
        ("Hidden", "hidden"),
    ]
    for (title, field) in bool_options:
        settings_form += '<h4>%s</h4>' % title
        if direct_dict.get(field, False):
            choices = _valid_bool + _reset_choice
        else:
            choices = _valid_bool + _keep_choice
        for (key, val) in choices:
            checked, inherit_note = '', ''
            if settings_dict.get(field, False) == val:
                checked = "checked"
            if direct_dict.get(field, False) != \
                    settings_dict.get(field, False):
                inherit_note = '''&nbsp;<span class="warningtext iconspace">
Forced by a parent %(vgrid_label)s. Please disable there first if you want to
change the value here.</span>''' % settings_dict
            settings_form += '''
            <input type="radio" name="%s" value="%s" %s /> %s
''' % (field, val, checked, key)
        settings_form += '%s<br/>' % inherit_note
    settings_form += '<br/>'

    settings_form += '''
            <input type="submit" value="Save settings" />
        </fieldset>
    </form>
'''
    output_objects.append({
        'object_type': 'html_form',
        'text': settings_form % settings_dict
    })

    # Checking/fixing of missing components

    output_objects.append({
        'object_type': 'sectionheader',
        'text': "Repair/Add Components"
    })
    target_op = 'updatevgrid'
    csrf_token = make_csrf_token(configuration, form_method, target_op,
                                 client_id, csrf_limit)
    settings_dict.update({'target_op': target_op, 'csrf_token': csrf_token})
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        '''
      <form method="%(form_method)s" action="%(target_op)s.py">
        <input type="hidden" name="%(csrf_field)s" value="%(csrf_token)s" />
          <input type="hidden" name="vgrid_name" value="%(vgrid_name)s" />
          <input type="submit" value="Repair components" />
      </form>
''' % settings_dict
    })

    (owners_status, owners_direct) = vgrid_owners(vgrid_name, configuration,
                                                  False)
    if not owners_status:
        logger.error("failed to load owners for %s: %s" %
                     (vgrid_name, owners_direct))
        return (output_objects, returnvalues.SYSTEM_ERROR)
    (members_status, members_direct) = vgrid_members(vgrid_name, configuration,
                                                     False)
    if not members_status:
        logger.error("failed to load members for %s: %s" %
                     (vgrid_name, members_direct))
        return (output_objects, returnvalues.SYSTEM_ERROR)
    (resources_status,
     resources_direct) = vgrid_resources(vgrid_name, configuration, False)
    if not resources_status:
        logger.error("failed to load resources for %s: %s" %
                     (vgrid_name, resources_direct))
        return (output_objects, returnvalues.SYSTEM_ERROR)

    output_objects.append({
        'object_type': 'sectionheader',
        'text': "Delete %s " % vgrid_name
    })
    if len(owners_direct) > 1 or members_direct or resources_direct:
        output_objects.append({
            'object_type': 'html_form',
            'text': '''
To delete <b>%(vgrid)s</b> first remove all resources, members and owners
ending with yourself.
''' % {
                'vgrid': vgrid_name
            }
        })
    else:
        output_objects.append({
            'object_type': 'html_form',
            'text': '''
<p>As the last owner you can leave and delete <b>%(vgrid)s</b> including all
associated shared files and components.<br/>
</p>
<p class="warningtext">
You cannot undo such delete operations, so please use with great care!
</p>
''' % {
                'vgrid': vgrid_name
            }
        })
        target_op = "rmvgridowner"
        csrf_token = make_csrf_token(configuration, form_method, target_op,
                                     client_id, csrf_limit)
        js_name = 'rmlastvgridowner'
        helper = html_post_helper(
            js_name, '%s.py' % target_op, {
                'vgrid_name': vgrid_name,
                'cert_id': client_id,
                'flags': 'f',
                csrf_field: csrf_token
            })
        output_objects.append({'object_type': 'html_form', 'text': helper})
        output_objects.append({
            'object_type':
            'link',
            'destination':
            "javascript: confirmDialog(%s, '%s');" %
            (js_name, 'Really leave and delete %s?' % vgrid_name),
            'class':
            'removelink iconspace',
            'title':
            'Leave and delete %s' % vgrid_name,
            'text':
            'Leave and delete %s' % vgrid_name
        })

    # Spacing
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        '''
            <div class="vertical-spacer"></div>
    '''
    })

    return (output_objects, returnvalues.OK)
示例#28
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id)
    client_dir = client_id_dir(client_id)
    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)

    patterns = accepted['job_id']
    action = accepted['action'][-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 configuration.site_enable_jobs:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '''Job execution is not enabled on this system'''
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    if not action in valid_actions.keys():
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Invalid job action "%s" (only %s supported)' %
            (action, ', '.join(valid_actions.keys()))
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    new_state = valid_actions[action]

    # 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.mrsl_files_dir,
                        client_dir)) + os.sep

    status = returnvalues.OK
    filelist = []
    for pattern in patterns:
        pattern = pattern.strip()

        # Backward compatibility - all_jobs keyword should match all jobs

        if pattern == all_jobs:
            pattern = '*'

        # Check directory traversal attempts before actual handling to avoid
        # leaking information about file system layout while allowing
        # consistent error messages

        unfiltered_match = glob.glob(base_dir + pattern + '.mRSL')
        match = []
        for server_path in unfiltered_match:
            # IMPORTANT: path must be expanded to abs for proper chrooting
            abs_path = os.path.abspath(server_path)
            if not valid_user_path(configuration, abs_path, base_dir, True):

                # out of bounds - save user warning for later to allow
                # partial match:
                # ../*/* is technically allowed to match own files.

                logger.error(
                    '%s tried to use %s %s outside own home! (pattern %s)' %
                    (client_id, op_name, abs_path, pattern))
                continue

            # Insert valid job files in filelist for later treatment

            match.append(abs_path)

        # Now actually treat list of allowed matchings and notify if no
        # (allowed) match

        if not match:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '%s: You do not have any matching job IDs!' % pattern
            })
            status = returnvalues.CLIENT_ERROR
        else:
            filelist += match

    # job state change is hard on the server, limit

    if len(filelist) > 500:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Too many matching jobs (%s)!' % len(filelist)
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    changedstatusjobs = []

    for filepath in filelist:

        # Extract job_id from filepath (replace doesn't modify filepath)

        mrsl_file = filepath.replace(base_dir, '')
        job_id = mrsl_file.replace('.mRSL', '')

        changedstatusjob = {
            'object_type': 'changedstatusjob',
            'job_id': job_id
        }

        job_dict = unpickle(filepath, logger)
        if not job_dict:
            changedstatusjob['message'] = '''The file containing the
information for job id %s could not be opened! You can only %s your own
jobs!''' % (job_id, action)
            changedstatusjobs.append(changedstatusjob)
            status = returnvalues.CLIENT_ERROR
            continue

        changedstatusjob['oldstatus'] = job_dict['STATUS']

        # Is the job status compatible with action?

        possible_cancel_states = [
            'PARSE', 'QUEUED', 'RETRY', 'EXECUTING', 'FROZEN'
        ]
        if action == 'cancel' and \
               not job_dict['STATUS'] in possible_cancel_states:
            changedstatusjob['message'] = \
                'You can only cancel jobs with status: %s.'\
                 % ' or '.join(possible_cancel_states)
            status = returnvalues.CLIENT_ERROR
            changedstatusjobs.append(changedstatusjob)
            continue
        possible_freeze_states = ['QUEUED', 'RETRY']
        if action == 'freeze' and \
               not job_dict['STATUS'] in possible_freeze_states:
            changedstatusjob['message'] = \
                'You can only freeze jobs with status: %s.'\
                 % ' or '.join(possible_freeze_states)
            status = returnvalues.CLIENT_ERROR
            changedstatusjobs.append(changedstatusjob)
            continue
        possible_thaw_states = ['FROZEN']
        if action == 'thaw' and \
               not job_dict['STATUS'] in possible_thaw_states:
            changedstatusjob['message'] = \
                'You can only thaw jobs with status: %s.'\
                 % ' or '.join(possible_thaw_states)
            status = returnvalues.CLIENT_ERROR
            changedstatusjobs.append(changedstatusjob)
            continue

        # job action is handled by changing the STATUS field, notifying the
        # job queue and making sure the server never submits jobs with status
        # FROZEN or CANCELED.

        # file is repickled to ensure newest information is used, job_dict
        # might be old if another script has modified the file.

        if not unpickle_and_change_status(filepath, new_state, logger):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Job status could not be changed to %s!' % new_state
            })
            status = returnvalues.SYSTEM_ERROR

        # Avoid key error and make sure grid_script gets expected number of
        # arguments

        if 'UNIQUE_RESOURCE_NAME' not in job_dict:
            job_dict['UNIQUE_RESOURCE_NAME'] = \
                'UNIQUE_RESOURCE_NAME_NOT_FOUND'
        if 'EXE' not in job_dict:
            job_dict['EXE'] = 'EXE_NAME_NOT_FOUND'

        # notify queue

        if not send_message_to_grid_script(
                'JOBACTION ' + job_id + ' ' + job_dict['STATUS'] + ' ' +
                new_state + ' ' + job_dict['UNIQUE_RESOURCE_NAME'] + ' ' +
                job_dict['EXE'] + '\n', logger, configuration):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '''Error sending message to grid_script,
job may still be in the job queue.'''
            })
            status = returnvalues.SYSTEM_ERROR
            continue

        changedstatusjob['newstatus'] = new_state
        changedstatusjobs.append(changedstatusjob)

    output_objects.append({
        'object_type': 'changedstatusjobs',
        'changedstatusjobs': changedstatusjobs
    })
    return (output_objects, status)
示例#29
0
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]
    output_objects.append({
        'object_type': 'header',
        'text': 'Remove Resource Owner'
    })
    (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)

    unique_resource_name = accepted['unique_resource_name'][-1]
    cert_id = accepted['cert_id'][-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 is_owner(client_id, unique_resource_name,
                    configuration.resource_home, logger):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'You must be an owner of %s to remove another owner!' %
            unique_resource_name
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # is_owner incorporates unique_resource_name verification - no need to
    # specifically check for illegal directory traversal

    if not is_user(cert_id, configuration.mig_server_home):
        output_objects.append({'object_type': 'error_text', 'text'
                              : '%s is not a valid %s user!' % \
                                (cert_id, configuration.short_title) })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # reject remove if cert_id is not an owner

    if not resource_is_owner(unique_resource_name, cert_id, configuration):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '%s is not an owner of %s.' % (cert_id, unique_resource_name)
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Remove owner

    (rm_status, rm_msg) = resource_remove_owners(configuration,
                                                 unique_resource_name,
                                                 [cert_id])
    if not rm_status:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Could not remove owner, reason: %s' % rm_msg
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    output_objects.append({
        'object_type':
        'text',
        'text':
        '%s was successfully removed and is no longer an owner of %s!' %
        (cert_id, unique_resource_name)
    })
    output_objects.append({'object_type': 'link', 'destination':
                        'resadmin.py?unique_resource_name=%s' % \
                           unique_resource_name, 'class': 'adminlink iconspace',
                           'title': 'Administrate resource',
                           'text': 'Manage resource'})
    return (output_objects, returnvalues.OK)
示例#30
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id)
    client_dir = client_id_dir(client_id)
    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)

    patterns = accepted['job_id']

    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 configuration.site_enable_jobs:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '''Job execution is not enabled on this system'''
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    # 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.mrsl_files_dir,
                        client_dir)) + os.sep

    status = returnvalues.OK
    filelist = []
    for pattern in patterns:
        pattern = pattern.strip()

        # Backward compatibility - all_jobs keyword should match all jobs

        if pattern == all_jobs:
            pattern = '*'

        # Check directory traversal attempts before actual handling to
        # avoid leaking information about file system layout while
        # allowing consistent error messages

        unfiltered_match = glob.glob(base_dir + pattern + '.mRSL')
        match = []
        for server_path in unfiltered_match:
            # IMPORTANT: path must be expanded to abs for proper chrooting
            abs_path = os.path.abspath(server_path)
            if not valid_user_path(configuration, abs_path, base_dir, True):

                # out of bounds - save user warning for later to allow
                # partial match:
                # ../*/* is technically allowed to match own files.

                logger.warning('%s tried to %s restricted path %s ! (%s)' %
                               (client_id, op_name, abs_path, pattern))
                continue

            # Insert valid job files in filelist for later treatment

            match.append(abs_path)

        # Now actually treat list of allowed matchings and notify if no
        # (allowed) match^I

        if not match:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '%s: You do not have any matching job IDs!' % pattern
            })
            status = returnvalues.CLIENT_ERROR
        else:
            filelist += match

    # job feasibility is hard on the server, limit

    if len(filelist) > 100:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Too many matching jobs (%s)!' % len(filelist)
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    checkcondjobs = []

    for filepath in filelist:

        # Extract job_id from filepath (replace doesn't modify filepath)

        mrsl_file = filepath.replace(base_dir, '')
        job_id = mrsl_file.replace('.mRSL', '')

        checkcondjob = {'object_type': 'checkcondjob', 'job_id': job_id}

        dict = unpickle(filepath, logger)
        if not dict:
            checkcondjob['message'] = \
                                    ('The file containing the information ' \
                                     'for job id %s could not be opened! ' \
                                     'You can only check feasibility of ' \
                                     'your own jobs!'
                                     ) % job_id
            checkcondjobs.append(checkcondjob)
            status = returnvalues.CLIENT_ERROR
            continue

        # Is the job status pending?

        possible_check_states = ['QUEUED', 'RETRY', 'FROZEN']
        if not dict['STATUS'] in possible_check_states:
            checkcondjob['message'] = \
                'You can only check feasibility of jobs with status: %s.'\
                 % ' or '.join(possible_check_states)
            checkcondjobs.append(checkcondjob)
            continue

        # Actually check feasibility
        feasible_res = job_feasibility(configuration, dict)
        checkcondjob.update(feasible_res)
        checkcondjobs.append(checkcondjob)

    output_objects.append({
        'object_type': 'checkcondjobs',
        'checkcondjobs': checkcondjobs
    })
    return (output_objects, status)