Ejemplo n.º 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 STATUS 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]
    exe_name_list = accepted["exe_name"]
    all = accepted["all"][-1].lower() == "true"
    parallel = accepted["parallel"][-1].lower() == "true"

    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 get status for 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=status_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()

    for (exe_name, task_list) in workers:
        (status, msg) = task_list[0].finish()
        output_objects.append({"object_type": "header", "text": "Status exe"})
        if not status:
            output_objects.append({"object_type": "error_text", "text": "Problems getting exe status: %s" % msg})
            exit_status = returnvalues.SYSTEM_ERROR
        else:
            output_objects.append({"object_type": "text", "text": "Status command run, output: %s" % msg})
    return (output_objects, exit_status)
Ejemplo n.º 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)
  
    status = returnvalues.OK
  
    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Job Manager'
    title_entry['style'] = css_tmpl(configuration)
    title_entry['javascript'] = js_tmpl()
  
    output_objects.append({'object_type': 'header', 'text': 'Job Manager'})
    output_objects.append({'object_type': 'html_form', 'text': html_pre()})
    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)
Ejemplo n.º 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)
    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 correct_handler('POST'):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Only accepting POST requests to prevent unintended updates'})
        return (output_objects, returnvalues.CLIENT_ERROR)

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

    output_objects.append({'object_type': 'header', 'text'
                          : 'Trying to Update resource configuration'})

    if not is_owner(client_id, unique_resource_name,
                    configuration.resource_home, logger):
        logger.error(client_id + ' is not an owner of '
                      + unique_resource_name + ': update rejected!')
        output_objects.append({'object_type': 'error_text', 'text'
                              : 'You must be an owner of '
                               + unique_resource_name
                               + ' to update the configuration!'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # TODO: race if two confs are uploaded concurrently!

    host_url, host_identifier = unique_resource_name.rsplit('.', 1)
    pending_file = os.path.join(configuration.resource_home,
                            unique_resource_name, 'config.tmp')

    # write new proposed config file to disk
    try:
        logger.info('write to file: %s' % pending_file)
        if not write_file(resconfig, pending_file, logger):
                output_objects.append({'object_type': 'error_text',
                        'text': 'Could not write: %s' % pending_file})
                return (output_objects, returnvalues.SYSTEM_ERROR)
    except Exception, err:
        logger.error('Resource conf %s could not be written: %s' % \
                     (pending_file, err))
        output_objects.append({'object_type': 'error_text', 'text':
                               'Could not write configuration!'})
        return (output_objects, returnvalues.SYSTEM_ERROR)
Ejemplo n.º 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, 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)

    vgrid_name = accepted['vgrid_name'][-1]
    path = accepted['path'][-1]
        
    if not vgrid_is_owner_or_member(vgrid_name, client_id,
                                    configuration):
        output_objects.append({'object_type': 'error_text', 'text':
                               '''You must be an owner or member of %s %s to
access the private files.''' % (vgrid_name, configuration.site_vgrid_label)})
        return (output_objects, returnvalues.CLIENT_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.vgrid_private_base,
                                            vgrid_name)) + os.sep

    # Strip leading slashes to avoid join() throwing away prefix 

    rel_path = path.lstrip(os.sep)
    real_path = os.path.abspath(os.path.join(base_dir, rel_path))

    if not valid_user_path(real_path, base_dir, True):
        output_objects.append({'object_type': 'error_text', 'text':
                               '''You are not allowed to use paths outside %s
private files dir.''' % configuration.site_vgrid_label})
        return (output_objects, returnvalues.CLIENT_ERROR)
    
    try:
        private_fd = open(real_path, 'rb')
        entry = {'object_type': 'binary',
                 'data': private_fd.read()}
        # Cut away all the usual web page formatting to show only contents
        output_objects = [{'object_type': 'start', 'headers': []}, entry,
                          {'object_type': 'script_status'},
                          {'object_type': 'end'}]
        private_fd.close()
    except Exception, exc:
        output_objects.append({'object_type': 'error_text', 'text'
                              : 'Error reading %s private file (%s)'
                               % (configuration.site_vgrid_label, exc)})
        return (output_objects, returnvalues.SYSTEM_ERROR)
Ejemplo n.º 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)
    output_objects.append({"object_type": "header", "text": "%s Virtual Desktop" % configuration.short_title})
    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)

    title_entry = find_entry(output_objects, "title")
    title_entry["text"] = "Virtual Machines"

    if not configuration.site_enable_vmachines:
        output_objects.append(
            {
                "object_type": "text",
                "text": """Virtual machines are disabled on this site.
Please contact the Grid admins %s if you think they should be enabled.
"""
                % configuration.admin_email,
            }
        )
        return (output_objects, returnvalues.OK)

    settings_dict = load_settings(client_id, configuration)
    if not settings_dict or not settings_dict.has_key("VNCDISPLAY"):
        logger.info("Settings dict does not have VNCDISPLAY key - using default")
        (vnc_display_width, vnc_display_height) = (1024, 768)
    else:
        (vnc_display_width, vnc_display_height) = settings_dict["VNCDISPLAY"]

    # Make room for vnc control menu

    vnc_menu_height = 24
    vnc_display_height += vnc_menu_height
    password = vms.vnc_jobid(accepted["job_id"][0])

    # Do an "intoN" then map to acsii

    output_objects.append(
        {
            "object_type": "html_form",
            "text": vms.popup_snippet()
            + vms.vnc_applet(configuration, vnc_display_width, vnc_display_height, password),
        }
    )

    return (output_objects, status)
Ejemplo n.º 6
0
def main(client_id, user_arguments_dict):
    """ main """

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

    output_objects.append({'object_type': 'text', 'text'
                          : '--------- Trying to STOP frontend ----------'
                          })
    defaults = signature()[1]
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
        )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    if not correct_handler('POST'):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Only accepting POST requests to prevent unintended updates'})
        return (output_objects, returnvalues.CLIENT_ERROR)

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

    logger.info('%s attempts to stop frontend at %s', client_id,
                unique_resource_name)

    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 '
                               + unique_resource_name
                               + ' to stop the resource frontend!'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    (status, msg) = stop_resource(unique_resource_name,
                                  configuration.resource_home, logger)
    if not status:
        output_objects.append({'object_type': 'error_text', 'text'
                              : '%s. Error stopping resource' % msg})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # everything ok

    output_objects.append({'object_type': 'text', 'text': '%s' % msg})
    return (output_objects, returnvalues.OK)
Ejemplo n.º 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)
    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,
        )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    if not correct_handler('POST'):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Only accepting POST requests to prevent unintended updates'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    save_as_default = (accepted['save_as_default'][-1] != 'False')
    external_dict = get_keywords_dict(configuration)
    mrsl = fields_to_mrsl(configuration, user_arguments_dict, external_dict)

    tmpfile = None

    # 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

    # save to temporary file

    try:
        (filehandle, real_path) = tempfile.mkstemp(text=True)
        relative_path = os.path.basename(real_path)
        os.write(filehandle, mrsl)
        os.close(filehandle)
    except Exception, err:
        output_objects.append({'object_type': 'error_text',
                               'text':
                               'Failed to write temporary mRSL file: %s' % \
                               err})
        return (output_objects, returnvalues.SYSTEM_ERROR)
Ejemplo n.º 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, 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)

    # 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)
Ejemplo n.º 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)

    output_objects.append({'object_type': 'text', 'text'
                          : '--------- Trying to get STATUS for frontend ----------'
                          })

    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]

    logger.info('%s attempts to get status for frontend at %s',
                client_id, unique_resource_name)

    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 '
                               + unique_resource_name
                               + ' to get status for the resource frontend!'
                              })
        return (output_objects, returnvalues.CLIENT_ERROR)

    (status, msg) = status_resource(unique_resource_name,
                                    configuration.resource_home, logger)
    if not status:
        output_objects.append({'object_type': 'error_text', 'text'
                              : '%s. Error getting resource status.'
                               % msg})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # everything ok

    output_objects.append({'object_type': 'text', 'text': '%s' % msg})
    return (output_objects, returnvalues.OK)
Ejemplo n.º 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)
    defaults = signature()[1]
    output_objects.append({'object_type': 'header', 'text'
                          : 'Show runtime environment details'})
    (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 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)

    if not is_runtime_environment(re_name, configuration):
        output_objects.append({'object_type': 'error_text', 'text'
                               : "'%s' is not an existing runtime environment!"
                               % re_name})
        return (output_objects, returnvalues.CLIENT_ERROR)

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Runtime environment details'

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

    output_objects.append(build_reitem_object(configuration, re_dict))

    return (output_objects, returnvalues.OK) 
Ejemplo n.º 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, 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)

    user_dir = os.path.join(configuration.user_home, 
                            client_id_dir(client_id))

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'ARC Queues'
    output_objects.append({'object_type': 'header', 'text'
                          : 'Available ARC queues'})

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

    # could factor out from here, to be usable from outside
    if not configuration.arc_clusters:
        output_objects.append({'object_type': 'error_text', 'text':
                               'No ARC support!'})
        return (output_objects, returnvalues.ERROR)
    try:
        session = arc.Ui(user_dir)
        queues = session.getQueues()

    except arc.NoProxyError, err:
        output_objects.append({'object_type': 'error_text', 'text'
                              : 'Error while retrieving: %s' % err.what()
                              })
        output_objects += arc.askProxy()
        return (output_objects, returnvalues.ERROR)
Ejemplo n.º 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)
    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)
    
    status = returnvalues.OK

    all_paths = accepted['path']
    entry_path = all_paths[-1]
    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'File Manager'
    title_entry['style'] = css_tmpl(configuration)
    if 'submitjob' in extract_menu(configuration, title_entry):
        enable_submit = 'true'
    else:
        enable_submit = 'false'
    title_entry['javascript'] = js_tmpl(entry_path, enable_submit,
                                        str(configuration.site_enable_preview))
    
    output_objects.append({'object_type': 'header', 'text': 'File Manager' })
    output_objects.append({'object_type': 'html_form', 'text':
                           html_tmpl(configuration, title_entry)})

    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)
Ejemplo n.º 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, op_header=False)

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Resource details'
    output_objects.append({'object_type': 'header', 'text'
                          : 'Show resource details'})

    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)
    resource_list = accepted['unique_resource_name']
    status = returnvalues.OK
    visible_res = user_visible_res_confs(configuration, client_id)
    allowed_vgrids = user_allowed_vgrids(configuration, client_id)

    for visible_res_name in resource_list:
        if not visible_res_name in visible_res.keys():
            logger.warning('User %s not allowed to view %s (%s)' % \
                           (client_id, visible_res_name, visible_res.keys()))
            output_objects.append({'object_type': 'error_text',
                                   'text': 'invalid resource %s' % \
                                   visible_res_name})
            continue
        res_dict = visible_res[visible_res_name]
        res_item = build_resitem_object_from_res_dict(configuration,
                                                      visible_res_name,
                                                      res_dict,
                                                      allowed_vgrids)
        output_objects.append(res_item)
        
    return (output_objects, status)
Ejemplo n.º 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)
    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)

    # 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

    pid = 0
    pidfile = os.path.join(base_dir, '.Xvnc4.pid')
    try:
        fd = open(pidfile, 'r')
        pid = int(fd.readline())
        fd.close()
        os.remove(pidfile)
        os.kill(pid, 9)
        output_objects.append({'object_type': 'text', 'text'
                              : 'stopped vnc'})
    except Exception, err:
        logger.error('Unable to extract pid and kill vnc process: %s'
                      % err)
        status = returnvalues.CLIENT_ERROR
        output_objects.append({'object_type': 'text', 'text'
                              : 'failed to stop vnc'})
Ejemplo n.º 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)
    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

    (status, msg) = vgrid_list(vgrid_name, 'triggers', configuration)
    if not 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)
Ejemplo n.º 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)

    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")

    (status, msg) = list_items_in_pickled_list(owners_file, logger)
    if not 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)
Ejemplo n.º 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)
    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)
Ejemplo n.º 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)
    output_objects.append({'object_type': 'header', 'text':
                           '%s Request Virtual Machine' % \
                           configuration.short_title})
    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)

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Virtual Machines'

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

    build_form = '''
<form method="post" action="vmachines.py">
<input type="hidden" name="output_format" value="html">
<input type="hidden" name="action" value="create">

<table style="margin: 0px; width: 100%;">
<tr>
  <td style="width: 20%;">Machine name</td>
  <td>
  <input type="text" name="machine_name" size="30" value="MyVirtualDesktop">
  </td>
</tr>
</table>

<fieldset>
<legend><input type="radio" name="machine_type" value="pre" checked="checked">Prebuilt</legend>
<table>
<tr>
  <td style="width: 20%;">Choose a OS version</td>
  <td>
  
<select name="os">
'''
    for os in vms.available_os_list(configuration):
        build_form += '<option value="%s">%s</option>\n' % \
                      (os, os.capitalize())
    build_form += '''
</select>

  </td>
</tr>
<tr>
  <td>Choose a machine image</td>
  <td>
  
<select name="flavor">
'''
    for flavor in vms.available_flavor_list(configuration):
        build_form += '<option value="%s">%s</option>\n' % \
                      (flavor, flavor.capitalize())
    build_form += """
</select>

  </td>
</tr>
<tr>
  <td>Select a runtime environment providing the chosen OS and
flavor combination.
For Ubuntu systems you can typically just use a runtime env from the same year,
like VBOX3.1-IMAGES-2010-1 for ubuntu-10.* versions.</td>
  <td>
  
<input type="hidden" name="hypervisor_re" value="%s">
<select name="sys_re">
""" % configuration.vm_default_hypervisor_re
    for sys_re in vms.available_sys_re_list(configuration):
        build_form += '<option value="%s">%s</option>\n' % \
                      (sys_re, sys_re)
    build_form += '''
</select>

  </td>
</tr>
</table>

</fieldset>

<fieldset>
<legend><input type="radio" name="machine_type" value="custom" disabled>Custom:</legend>
<span class="warningtext">Custom builds are currently unavailable.</span>
<table>
<tr>
  <td style="width: 20%;">Software</td>
  <td>
<input type=text size=80 name="machine_software" readonly
value="iptables acpid x11vnc xorg gdm xfce4 gcc make netsurf python-openssl" />
  </td>
</tr>
</table>

</fieldset>
<input type="submit" value="Submit machine request!">

</form>
'''
    output_objects.append({'object_type': 'html_form', 'text': build_form})
    return (output_objects, status)
Ejemplo n.º 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)
    defaults = signature()[1]
    output_objects.append({'object_type': 'header', 'text'
                          : 'Remove %s Trigger' % \
                           configuration.site_vgrid_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)

    if not correct_handler('POST'):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Only accepting POST requests to prevent unintended updates'})
        return (output_objects, returnvalues.CLIENT_ERROR)

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

    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,
                                  configuration.site_vgrid_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 %(_label)s because it is a trigger of a parent %(_label)s. Removal must
be performed from the most significant %(_label)s possible.'''
                               % {'rule_id': rule_id,
                                  '_label': configuration.site_vgrid_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,
                              configuration.site_vgrid_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)
Ejemplo n.º 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]
    title_entry = find_entry(output_objects, 'title')
    label = "%s" % configuration.site_vgrid_label
    title_entry['text'] = "Save %s Settings" % label
    output_objects.append(
        {'object_type': 'header', 'text': 'Save %s Settings' % 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)

    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)

    vgrid_name = accepted['vgrid_name'][-1].strip()

    new_settings = {'vgrid_name': vgrid_name}

    # 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,
                                  new_settings.items(), 'settings',
                                  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})

    user_map = get_full_user_map(configuration)
    user_dict = user_map.get(client_id, None)
    # Optional site-wide limitation of manage vgrid permission
    if not user_dict or \
            not vgrid_manage_allowed(configuration, user_dict):
        logger.warning("user %s is not allowed to manage vgrids!" % client_id)
        output_objects.append(
            {'object_type': 'error_text', 'text':
             'Only privileged users can manage %ss' % label})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Check if this owner is allowed to change settings

    (allow_status, allow_msg) = allow_settings_adm(configuration, vgrid_name,
                                                   client_id)
    if not allow_status:
        output_objects.append({'object_type': 'error_text', 'text': allow_msg})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Build current settings, leaving out unset ones for default/inherit
    if not keyword_auto in accepted['description']:
        new_settings['description'] = '\n'.join(
            accepted['description']).strip()
    if not keyword_auto in accepted['visible_owners']:
        visible_owners = accepted['visible_owners'][-1]
        if visible_owners in _valid_visible:
            new_settings['visible_owners'] = visible_owners
        else:
            msg = "invalid visible_owners value: %s" % visible_owners
            logger.warning(msg)
            output_objects.append({'object_type': 'error_text', 'text': msg})
            return (output_objects, returnvalues.CLIENT_ERROR)
    if not keyword_auto in accepted['visible_members']:
        visible_members = accepted['visible_members'][-1]
        if visible_members in _valid_visible:
            new_settings['visible_members'] = visible_members
        else:
            msg = "invalid visible_members value: %s" % visible_members
            logger.warning(msg)
            output_objects.append({'object_type': 'error_text', 'text': msg})
            return (output_objects, returnvalues.CLIENT_ERROR)
    if not keyword_auto in accepted['visible_resources']:
        visible_resources = accepted['visible_resources'][-1]
        if visible_resources in _valid_visible:
            new_settings['visible_resources'] = visible_resources
        else:
            msg = "invalid visible_resources value: %s" % visible_resources
            logger.warning(msg)
            output_objects.append({'object_type': 'error_text', 'text': msg})
            return (output_objects, returnvalues.CLIENT_ERROR)
    if not keyword_auto in accepted['create_sharelink']:
        create_sharelink = accepted['create_sharelink'][-1]
        if create_sharelink in _valid_sharelink:
            new_settings['create_sharelink'] = create_sharelink
        else:
            msg = "invalid create_sharelink value: %s" % create_sharelink
            logger.warning(msg)
            output_objects.append({'object_type': 'error_text', 'text': msg})
            return (output_objects, returnvalues.CLIENT_ERROR)

    if not _keyword_auto_int in accepted['request_recipients']:
        try:
            request_recipients = accepted['request_recipients'][-1]
            request_recipients = int(request_recipients)
            new_settings['request_recipients'] = request_recipients
        except ValueError:
            msg = "invalid request_recipients value: %s" % request_recipients
            logger.warning(msg)
            output_objects.append({'object_type': 'error_text', 'text': msg})
            return (output_objects, returnvalues.CLIENT_ERROR)
    if not _keyword_auto_int in accepted['restrict_settings_adm']:
        try:
            restrict_settings_adm = accepted['restrict_settings_adm'][-1]
            restrict_settings_adm = int(restrict_settings_adm)
            new_settings['restrict_settings_adm'] = restrict_settings_adm
        except ValueError:
            msg = "invalid restrict_settings_adm value: %s" % \
                  restrict_settings_adm
            logger.warning(msg)
            output_objects.append({'object_type': 'error_text', 'text': msg})
            return (output_objects, returnvalues.CLIENT_ERROR)
    else:
        restrict_settings_adm = default_vgrid_settings_limit
    if not _keyword_auto_int in accepted['restrict_owners_adm']:
        try:
            restrict_owners_adm = accepted['restrict_owners_adm'][-1]
            restrict_owners_adm = int(restrict_owners_adm)
            new_settings['restrict_owners_adm'] = restrict_owners_adm
        except ValueError:
            msg = "invalid restrict_owners_adm value: %s" % restrict_owners_adm
            logger.warning(msg)
            output_objects.append({'object_type': 'error_text', 'text': msg})
            return (output_objects, returnvalues.CLIENT_ERROR)
    else:
        restrict_owners_adm = default_vgrid_settings_limit
    if not _keyword_auto_int in accepted['restrict_members_adm']:
        try:
            restrict_members_adm = accepted['restrict_members_adm'][-1]
            restrict_members_adm = int(restrict_members_adm)
            new_settings['restrict_members_adm'] = restrict_members_adm
        except ValueError:
            msg = "invalid restrict_members_adm value: %s" % \
                  restrict_members_adm
            logger.warning(msg)
            output_objects.append({'object_type': 'error_text', 'text': msg})
            return (output_objects, returnvalues.CLIENT_ERROR)
    if not _keyword_auto_int in accepted['restrict_resources_adm']:
        try:
            restrict_resources_adm = accepted['restrict_resources_adm'][-1]
            restrict_resources_adm = int(restrict_resources_adm)
            new_settings['restrict_resources_adm'] = restrict_resources_adm
        except ValueError:
            msg = "invalid restrict_resources_adm value: %s" % \
                  restrict_resources_adm
            logger.warning(msg)
            output_objects.append({'object_type': 'error_text', 'text': msg})
            return (output_objects, returnvalues.CLIENT_ERROR)
    if keyword_auto in accepted['write_shared_files']:
        write_shared_files = keyword_members
    else:
        write_shared_files = accepted['write_shared_files'][-1]
        if write_shared_files in _valid_write_access:
            new_settings['write_shared_files'] = write_shared_files
        else:
            msg = 'unknown write_shared_files value: %s' % write_shared_files
            output_objects.append({'object_type': 'error_text', 'text': msg})
            return (output_objects, returnvalues.CLIENT_ERROR)
    if keyword_auto in accepted['write_priv_web']:
        write_priv_web = keyword_owners
    else:
        write_priv_web = accepted['write_priv_web'][-1]
        if write_priv_web in _valid_write_access:
            new_settings['write_priv_web'] = write_priv_web
        else:
            msg = 'unknown write_priv_web value: %s' % write_priv_web
            output_objects.append({'object_type': 'error_text', 'text': msg})
            return (output_objects, returnvalues.CLIENT_ERROR)
    if keyword_auto in accepted['write_pub_web']:
        write_pub_web = keyword_owners
    else:
        write_pub_web = accepted['write_pub_web'][-1]
        if write_pub_web in _valid_write_access:
            new_settings['write_pub_web'] = write_pub_web
        else:
            msg = 'unknown write_pub_web value: %s' % write_pub_web
            output_objects.append({'object_type': 'error_text', 'text': msg})
            return (output_objects, returnvalues.CLIENT_ERROR)
    if not keyword_auto in accepted['hidden']:
        hidden = accepted['hidden'][-1]
        is_hidden = False
        if hidden.lower() in ("true", "1", "yes"):
            is_hidden = True
        new_settings['hidden'] = is_hidden

    if restrict_settings_adm > restrict_owners_adm:
        output_objects.append({'object_type': 'html_form', 'text': '''
<span class="warningtext">Warning: Restrict owner administration may still be
circumvented by some owners unless Restrict settings administration is set to
a lower or equal number.</span>'''})

    # TODO: remove this bail-out once we've implemented wider web write access
    if write_priv_web != keyword_owners or write_pub_web != keyword_owners:
        output_objects.append(
            {'object_type': 'error_text', 'text':
             """%s does not yet support changing write access to the
private and public web pages""" % configuration.short_title})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Handle any write access limit changes first

    (load_old, old_settings) = vgrid_settings(vgrid_name, configuration,
                                              recursive=False, as_dict=True)
    if not load_old:
        output_objects.append(
            {'object_type': 'error_text', 'text':
             'failed to load saved %s settings' % vgrid_name})
        return (output_objects, returnvalues.SYSTEM_ERROR)
    old_write_shared = old_settings.get('write_shared_files', keyword_members)
    contact_text = """Please contact the %s admins if you then still get refused.
Then it is probably because they need to upgrade your %s %s to the new layout
format first.""" % (configuration.short_title, vgrid_name, label)

    # TODO: remove this bail-out once we've implemented owner-only write access
    if write_shared_files == keyword_owners:
        output_objects.append(
            {'object_type': 'error_text', 'text':
             """Owner-only write access is not supported yet"""})
        return (output_objects, returnvalues.CLIENT_ERROR)
    elif old_write_shared == write_shared_files:
        logger.debug("write shared status (%s) is unchanged" %
                     old_write_shared)
    else:
        if not vgrid_allow_restrict_write(vgrid_name, write_shared_files,
                                          configuration):
            if write_shared_files != keyword_members:
                # Refuse if any child vgrid is writable
                output_objects.append(
                    {'object_type': 'error_text', 'text': """Refused to tighten
write access for %s - layout or child prevents it! You need to make sure all
child %ss have at least as strict write access before you can proceed to
restrict %s. %s.""" % (vgrid_name, label, vgrid_name, contact_text)})
            else:
                # Refuse if parent vgrid restricts write
                output_objects.append(
                    {'object_type': 'error_text', 'text': """Refused to make
%s fully writable - layout or parent prevents it! You need to remove write
restrictions on all parent %ss before you can make %s writable. %s.""" %
                     (vgrid_name, label, vgrid_name, contact_text)})

            return (output_objects, returnvalues.CLIENT_ERROR)

        if not vgrid_restrict_write(vgrid_name, write_shared_files,
                                    configuration):
            if write_shared_files != keyword_members:
                output_objects.append(
                    {'object_type': 'error_text', 'text': """Failed to restrict
write to %s on %s shared files""" % (write_shared_files.lower(), vgrid_name)})
            else:
                output_objects.append(
                    {'object_type': 'error_text', 'text': """Failed to make %s
 fully writable""" % vgrid_name})

            return (output_objects, returnvalues.SYSTEM_ERROR)

    # format as list of tuples to fit usual form and then pickle

    (set_status, set_msg) = vgrid_set_settings(configuration, vgrid_name,
                                               new_settings.items())
    if not set_status:
        output_objects.append({'object_type': 'error_text', 'text': '%s'
                               % set_msg})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    output_objects.append({'object_type': 'text', 'text':
                           'Settings saved for %s %s!' % (vgrid_name, label)})
    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)
Ejemplo n.º 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)
    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 not job_dict.has_key('UNIQUE_RESOURCE_NAME'):
            job_dict['UNIQUE_RESOURCE_NAME'] = \
                'UNIQUE_RESOURCE_NAME_NOT_FOUND'
        if not job_dict.has_key('EXE'):
            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)
Ejemplo n.º 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)
    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)
Ejemplo n.º 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)
    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)
Ejemplo n.º 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, op_header=False)
    defaults = signature()[1]
    title_entry = find_entry(output_objects, 'title')
    label = "%s" % configuration.site_vgrid_label
    title_entry['text'] = "Remove %s Member" % label
    output_objects.append({
        'object_type': 'header',
        'text': 'Remove %s Member' % 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]
    cert_id = accepted['cert_id'][-1]
    cert_dir = client_id_dir(cert_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)

    # always allow member to remove self
    if client_id != cert_id:
        user_map = get_full_user_map(configuration)
        user_dict = user_map.get(client_id, None)
        # Optional site-wide limitation of manage vgrid permission
        if not user_dict or \
                not vgrid_manage_allowed(configuration, user_dict):
            logger.warning("user %s is not allowed to manage vgrids!" %
                           client_id)
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Only privileged users can manage %ss' % label
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

        # make sure vgrid settings allow this owner to edit other members
        (allow_status, allow_msg) = allow_members_adm(configuration,
                                                      vgrid_name, client_id)
        if not allow_status:
            output_objects.append({
                'object_type': 'error_text',
                'text': allow_msg
            })
            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, _) = \
        init_vgrid_script_add_rem(vgrid_name, client_id, cert_id,
                                  'member', configuration)
    if not ret_val:
        output_objects.append({'object_type': 'error_text', 'text': msg})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # don't remove if not a member

    if not vgrid_is_member(vgrid_name, cert_id, configuration):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '%s is not a member of %s or a parent %s.' %
            (cert_id, vgrid_name, label)
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # owner of subvgrid?

    (list_status, subvgrids) = vgrid_list_subvgrids(vgrid_name, configuration)
    if not list_status:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Error getting list of sub%ss: %s' % (label, subvgrids)
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    # TODO: we DO allow ownership of sub vgrids with parent membership so we
    # should support the (cumbersome) relinking of vgrid shares here. Leave it
    # to user to do it manually for now with temporary removal of ownership

    for subvgrid in subvgrids:
        if vgrid_is_owner(subvgrid, cert_id, configuration, recursive=False):
            output_objects.append({
                'object_type': 'error_text',
                'text':
                """%(cert_id)s is already an owner of a sub-%(vgrid_label)s
('%(subvgrid)s'). While we DO support members being owners of
sub-%(vgrid_label)ss, we do not support removing parent %(vgrid_label)s members
at the moment. Please (temporarily) remove the person as owner of all
sub-%(vgrid_label)ss first and then try this operation again.""" % {
                    'cert_id': cert_id,
                    'subvgrid': subvgrid,
                    'vgrid_label': label
                }
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

    # Please note that base_dir must end in slash to avoid access to other
    # vgrid dirs when own name is a prefix of another name

    base_dir = os.path.abspath(
        os.path.join(configuration.vgrid_home, vgrid_name)) + os.sep

    # remove symlink from users home directory to vgrid directory

    # 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

    user_dir = os.path.abspath(os.path.join(configuration.user_home,
                                            cert_dir)) + os.sep

    dst = user_dir + vgrid_name
    try:
        os.remove(dst)
    except Exception, exc:

        # ouch, not good. Email admin?

        pass
Ejemplo n.º 25
0
def main(client_id, user_arguments_dict, environ=None):
    """Main function used by front end"""

    if environ is None:
        environ = os.environ

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id)
    client_dir = client_id_dir(client_id)
    defaults = signature()[1]
    status = returnvalues.OK
    (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)

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

    # 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)
            })
    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)

        dst_mode = "wb"
        # IMPORTANT: path must be expanded to abs for proper chrooting
        abs_dest = os.path.abspath(os.path.join(base_dir, dst))
        relative_dst = abs_dest.replace(base_dir, '')
        if not valid_user_path(configuration, abs_dest, base_dir, True):
            logger.warning('%s tried to %s into restricted path %s ! (%s)' %
                           (client_id, op_name, abs_dest, dst))
            output_objects.append({
                'object_type': 'error_text',
                'text': "invalid destination: '%s'" % dst
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

    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:
            output_lines = []
            relative_path = abs_path.replace(base_dir, '')
            try:
                gdp_iolog(configuration, client_id, environ['REMOTE_ADDR'],
                          'accessed', [relative_path])
                fd = open(abs_path, 'r')

                # use file directly as iterator for efficiency

                for line in fd:
                    output_lines.append(line)
                fd.close()
            except Exception, exc:
                if not isinstance(exc, GDPIOLogError):
                    gdp_iolog(configuration,
                              client_id,
                              environ['REMOTE_ADDR'],
                              'accessed', [relative_path],
                              failed=True,
                              details=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:
                try:
                    gdp_iolog(configuration, client_id, environ['REMOTE_ADDR'],
                              'modified', [dst])
                    out_fd = open(abs_dest, dst_mode)
                    out_fd.writelines(output_lines)
                    out_fd.close()
                    logger.info('%s %s %s done' %
                                (op_name, abs_path, abs_dest))
                except Exception, exc:
                    if not isinstance(exc, GDPIOLogError):
                        gdp_iolog(configuration,
                                  client_id,
                                  environ['REMOTE_ADDR'],
                                  'modified', [dst],
                                  error=True,
                                  details=exc)
                    output_objects.append({
                        'object_type': 'error_text',
                        'text': "write failed: '%s'" % exc
                    })
                    logger.error("%s: write failed on '%s': %s" %
                                 (op_name, abs_dest, exc))
                    status = returnvalues.SYSTEM_ERROR
                    continue
                output_objects.append({
                    'object_type':
                    'text',
                    'text':
                    "wrote %s to %s" % (relative_path, relative_dst)
                })
                # Prevent truncate after first write
                dst_mode = "ab+"
            else:
                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)

                # TODO: rip this hack out into real download handler?
                # Force download of files when output_format == 'file_format'
                # This will only work for the first file matching a glob when
                # using file_format.
                # And it is supposed to only work for one file.
                if user_arguments_dict.has_key('output_format'):
                    output_format = user_arguments_dict['output_format'][0]
                    if output_format == 'file':
                        output_objects.append({
                            'object_type':
                            'start',
                            'headers': [('Content-Disposition',
                                         'attachment; filename="%s";' %
                                         os.path.basename(abs_path))]
                        })
Ejemplo n.º 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, 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,
        require_user=False
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = '%s certificate account sign up' % \
                          configuration.short_title
    title_entry['skipmenu'] = True
    form_fields = ['cert_id', 'cert_name', 'organization', 'email', 'country',
                   'state', 'comment']
    title_entry['style']['advanced'] += account_css_helpers(configuration)
    add_import, add_init, add_ready = account_js_helpers(configuration,
                                                         form_fields)
    title_entry['script']['advanced'] += add_import
    title_entry['script']['init'] += add_init
    title_entry['script']['ready'] += add_ready
    title_entry['script']['body'] = "class='staticpage'"
#    output_objects.append({'object_type': 'html_form',
#                           'text': '''
# <div id="contextual_help">
#  <div class="help_gfx_bubble"><!-- graphically connect field with help text--></div>
#  <div class="help_message"><!-- filled by js --></div>
# </div>
# '''})
    header_entry = {'object_type': 'header', 'text':
                    'Welcome to the %s certificate account sign up page' %
                    configuration.short_title}
    output_objects.append(header_entry)

    # Redirect to reqcert page without certificate requirement but without
    # changing access method (CGI vs. WSGI).

    certreq_url = os.environ['REQUEST_URI'].replace('-bin', '-sid')
    certreq_url = os.path.join(os.path.dirname(certreq_url), 'reqcert.py')
    certreq_link = {'object_type': 'link', 'destination': certreq_url,
                    'text': 'Request a new %s certificate account' %
                            configuration.short_title}
    new_user = distinguished_name_to_user(client_id)

    # If cert auto create is on, add user without admin interaction

    form_method = 'post'
    csrf_limit = get_csrf_limit(configuration)
    fill_helpers = {'valid_name_chars': valid_name_chars,
                    'client_id': client_id,
                    'dn_max_len': dn_max_len,
                    'full_name': new_user.get('full_name', ''),
                    'organization': new_user.get('organization', ''),
                    'email': new_user.get('email', ''),
                    'state': new_user.get('state', ''),
                    'country': new_user.get('country', ''),
                    'site': configuration.short_title,
                    'form_method': form_method,
                    'csrf_field': csrf_field,
                    'csrf_limit': csrf_limit}
    if configuration.auto_add_cert_user == False:
        target_op = 'extcertaction'
    else:
        target_op = 'autocreate'
    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})
    fill_helpers.update({'site_signup_hint': configuration.site_signup_hint})

    html = """This page is
used to sign up for %(site)s with an existing certificate from a Certificate
Authority (CA) allowed for %(site)s.
You can use it if you already have a x509 certificate from another accepted CA.
In this way you can simply use your existing certificate for %(site)s access
instead of requesting a new one.
<br />
The page tries to auto load any certificate your browser provides and fill in
the fields accordingly, but in case it can't guess all
<span class=mandatory>mandatory</span> fields, you still need to fill in
those.<br />
Please enter any missing information below and press the Send button to submit
the external certificate sign up request to the %(site)s administrators.
<p class='criticaltext highlight_message'>
IMPORTANT: Please help us verify your identity by providing Organization and
Email data that we can easily validate!
</p>
%(site_signup_hint)s
<hr />
"""

    html += account_request_template(configuration, password=False)

    # TODO : remove this legacy version?
    html += """
<div style="height: 0; visibility: hidden; display: none;">
<!--OLD FORM-->
<div class=form_container>
<!-- use post here to avoid field contents in URL -->
<form method='%(form_method)s' action='%(target_op)s.py' onSubmit='return validate_form();'>
<input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
<table>
<!-- NOTE: javascript support for unicode pattern matching is lacking so we
           only restrict e.g. Full Name to words separated by space here. The
           full check takes place in the backend, but users are better of with
           sane early warnings than the cryptic backend errors.
-->
<tr><td class='mandatory label'>Certificate DN</td><td><input id='cert_id_field' type=text size=%(dn_max_len)s maxlength=%(dn_max_len)s name=cert_id value='%(client_id)s' required pattern='(/[a-zA-Z]+=[^/ ]+([ ][^/ ]+)*)+' title='The Distinguished Name field of your certificate, i.e. key=value pairs separated by slashes' /></td><td class=fill_space></td></tr>
<tr><td class='mandatory label'>Full name</td><td><input id='cert_name_field' type=text name=cert_name value='%(full_name)s' required pattern='[^ ]+([ ][^ ]+)+' title='Your full name, i.e. two or more names separated by space' /></td><td class=fill_space></td></tr>
<tr><td class='mandatory label'>Email address</td><td><input id='email_field' type=email name=email value='%(email)s' title='A valid email address that you read' /></td><td class=fill_space></td></tr>
<tr><td class='mandatory label'>Organization</td><td><input id='organization_field' type=text name=org value='%(organization)s' required pattern='[^ ]+([ ][^ ]+)*' title='Name of your organisation: one or more abbreviations or words separated by space' /></td><td class=fill_space></td></tr>
<tr><td class='mandatory label'>Two letter country-code</td><td><input id='country_field' type=text name=country minlength=2 maxlength=2 value='%(country)s' required pattern='[A-Z]{2}' title='The two capital letters used to abbreviate your country' /></td><td class=fill_space></td></tr>
<tr><td class='optional label'>State</td><td><input id='state_field' type=text name=state value='%(state)s' pattern='([A-Z]{2})?' maxlength=2 title='Leave empty or enter the capital 2-letter abbreviation of your state if you are a US resident' /></td><td class=fill_space></td></tr>
<tr><td class='optional label'>Comment or reason why you should<br />be granted a %(site)s certificate:</td><td><textarea id='comment_field' rows=4 name=comment title='A free-form comment where you can explain what you need the certificate for'></textarea></td><td class=fill_space></td></tr>
<tr><td class='label'><!-- empty area --></td><td><input id='submit_button' type='submit' value='Send' /></td><td class=fill_space></td></tr>
</table>
</form>
</div>
<!-- Hidden help text -->
<div id='help_text'>
  <div id='cert_id_help'>Must be the exact Distinguished Name (DN) of your certificate</div>
  <div id='cert_name_help'>Your full name, restricted to the characters in '%(valid_name_chars)s'</div>
  <div id='organization_help'>Organization name or acronym  matching email</div>
  <div id='email_help'>Email address associated with your organization if at all possible</div>
  <div id='country_help'>Country code of your organization and on the form DE/DK/GB/US/.. , <a href='https://en.wikipedia.org/wiki/ISO_3166-1'>help</a></div>
  <div id='state_help'>Optional 2-letter ANSI state code of your organization, please just leave empty unless it is in the US or similar, <a href='https://en.wikipedia.org/wiki/List_of_U.S._state_abbreviations'>help</a></div>
  <div id='comment_help'>Optional, but a short informative comment may help us verify your certificate needs and thus speed up our response.</div>
</div>
</div>
    """
    output_objects.append({'object_type': 'html_form', 'text':
                           html % fill_helpers})

    return (output_objects, returnvalues.OK)
Ejemplo n.º 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)

    output_objects.append({
        'object_type': 'header',
        'text': 'Virtual Machines'
    })
    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)

    machine_name = accepted['machine_name'][-1].strip()
    memory = int(accepted['memory'][-1])
    disk = int(accepted['disk'][-1])
    vgrid = [name.strip() for name in accepted['vgrid']]
    architecture = accepted['architecture'][-1].strip()
    cpu_count = int(accepted['cpu_count'][-1])
    cpu_time = int(accepted['cpu_time'][-1])
    op_sys = accepted['os'][-1].strip()
    flavor = accepted['flavor'][-1].strip()
    hypervisor_re = accepted['hypervisor_re'][-1].strip()
    sys_re = accepted['sys_re'][-1].strip()
    action = accepted['action'][-1].strip()

    if action in edit_actions and \
        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)

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Virtual Machines'

    # jquery support for confirmation on delete:
    (add_import, add_init, add_ready) = confirm_js(configuration)

    add_ready += '''
          $(".vm-tabs").tabs();
    '''
    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': confirm_html(configuration)
    })

    if not configuration.site_enable_vmachines:
        output_objects.append({
            'object_type':
            'text',
            'text':
            '''Virtual machines 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)

    machine_req = {
        'memory': memory,
        'disk': disk,
        'cpu_count': cpu_count,
        'cpu_time': cpu_time,
        'architecture': architecture,
        'vgrid': vgrid,
        'os': op_sys,
        'flavor': flavor,
        'hypervisor_re': hypervisor_re,
        'sys_re': sys_re
    }

    menu_items = ['vmrequest']

    # Html fragments

    submenu = render_menu(configuration,
                          menu_class='navsubmenu',
                          base_menu=[],
                          user_menu=menu_items)

    welcome_text = 'Welcome to your %s virtual machine management!' % \
                   configuration.short_title
    desc_text = '''On this page you can:
<ul>
    <li>Request Virtual Machines, by clicking on the button above</li>
    <li>See your virtual machines in the list below.</li>
    <li>Start, and connect to your Virtual Machine by clicking on it.</li>
    <li>Edit or delete your Virtual Machine from the Advanced tab.</li>
</ul>
'''

    output_objects.append({
        'object_type':
        'html_form',
        'text':
        '''
 <div id="confirm_dialog" title="Confirm" style="background:#fff;">
  <div id="confirm_text"><!-- filled by js --></div>
   <textarea cols="40" rows="4" id="confirm_input" style="display:none;"></textarea>
 </div>
'''
    })

    output_objects.append({'object_type': 'html_form', 'text': submenu})
    output_objects.append({
        'object_type': 'html_form',
        'text': '<p>&nbsp;</p>'
    })
    output_objects.append({
        'object_type': 'sectionheader',
        'text': welcome_text
    })
    output_objects.append({'object_type': 'html_form', 'text': desc_text})

    user_vms = vms.vms_list(client_id, configuration)
    if action == 'create':
        if not configuration.site_enable_vmachines:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "Virtual machines are disabled on this server"
            })
            status = returnvalues.CLIENT_ERROR
            return (output_objects, status)
        if not machine_name:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "requested build without machine name"
            })
            status = returnvalues.CLIENT_ERROR
            return (output_objects, status)
        elif machine_name in [vm["name"] for vm in user_vms]:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "requested machine name '%s' already exists!" % machine_name
            })
            status = returnvalues.CLIENT_ERROR
            return (output_objects, status)
        elif not flavor in vms.available_flavor_list(configuration):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "requested pre-built flavor not available: %s" % flavor
            })
            status = returnvalues.CLIENT_ERROR
            return (output_objects, status)
        elif not hypervisor_re in \
                vms.available_hypervisor_re_list(configuration):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "requested hypervisor runtime env not available: %s" %
                hypervisor_re
            })
        elif not sys_re in vms.available_sys_re_list(configuration):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "requested system pack runtime env not available: %s" % sys_re
            })
            status = returnvalues.CLIENT_ERROR
            return (output_objects, status)

        # TODO: support custom build of machine using shared/vmbuilder.py

        # request for existing pre-built machine

        logger.debug("create new vm: %s" % machine_req)
        (create_status, create_msg) = vms.create_vm(client_id, configuration,
                                                    machine_name, machine_req)
        if not create_status:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "requested virtual machine could not be created: %s" %
                create_msg
            })
            status = returnvalues.SYSTEM_ERROR
            return (output_objects, status)

    (action_status, action_msg, job_id) = (True, '', None)
    if action in ['start', 'stop', 'edit', 'delete']:
        if not configuration.site_enable_vmachines:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "Virtual machines are disabled on this server"
            })
            status = returnvalues.CLIENT_ERROR
            return (output_objects, status)
    if action == 'start':
        machine = {}
        for entry in user_vms:
            if machine_name == entry['name']:
                for name in machine_req.keys():
                    if isinstance(entry[name], basestring) and \
                            entry[name].isdigit():
                        machine[name] = int(entry[name])
                    else:
                        machine[name] = entry[name]
                break
        (action_status, action_msg, job_id) = \
            vms.enqueue_vm(client_id, configuration, machine_name,
                           machine)
    elif action == 'edit':
        if not machine_name in [vm['name'] for vm in user_vms]:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "No such virtual machine: %s" % machine_name
            })
            status = returnvalues.CLIENT_ERROR
            return (output_objects, status)
        (action_status, action_msg) = \
            vms.edit_vm(client_id, configuration, machine_name,
                        machine_req)
    elif action == 'delete':
        if not machine_name in [vm['name'] for vm in user_vms]:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "No such virtual machine: %s" % machine_name
            })
            status = returnvalues.CLIENT_ERROR
            return (output_objects, status)
        (action_status, action_msg) = \
            vms.delete_vm(client_id, configuration, machine_name)
    elif action == 'stop':

        # TODO: manage stop - use live I/O to create vmname.stop in job dir

        pass

    if not action_status:
        output_objects.append({
            'object_type': 'error_text',
            'text': action_msg
        })

    # List the machines here

    output_objects.append({
        'object_type': 'sectionheader',
        'text': 'Your machines:'
    })

    # Grab the vms available for the user

    machines = vms.vms_list(client_id, configuration)

    # Visual representation mapping of the machine state

    machine_states = {
        'EXECUTING': 'vm_running.jpg',
        'CANCELED': 'vm_off.jpg',
        'FAILED': 'vm_off.jpg',
        'FINISHED': 'vm_off.jpg',
        'UNKNOWN': 'vm_off.jpg',
        'QUEUED': 'vm_booting.jpg',
        'PARSE': 'vm_booting.jpg',
    }

    # Empirical upper bound on boot time in seconds used to decide between
    # desktop init and ready states

    boot_secs = 130

    # CANCELED/FAILED/FINISHED -> Powered Off
    # QUEUED -> Booting

    if len(machines) > 0:

        form_method = 'post'
        csrf_limit = get_csrf_limit(configuration)
        fill_helpers = {
            'form_method': form_method,
            'csrf_field': csrf_field,
            'csrf_limit': csrf_limit
        }
        target_op = 'vmachines'
        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})
        # Create a pretty list with start/edit/stop/connect links

        pretty_machines = \
            '<table style="border: 0; background: none;"><tr>'
        side_by_side = 3  # How many machines should be shown in a row?

        col = 0
        for machine in machines:

            # Machines on a row

            if col % side_by_side == 0:
                pretty_machines += '</tr><tr>'
            col += 1

            # Html format machine specifications in a fieldset

            password = '******'
            exec_time = 0
            if machine['job_id'] != 'UNKNOWN' and \
                    machine['status'] == 'EXECUTING':

                # TODO: improve on this time selection...
                # ... in distributed there is no global clock!

                exec_time = time.time() - 3600 \
                    - time.mktime(machine['execution_time'])
                password = vms.vnc_jobid(machine['job_id'])

            machine_specs = {}
            machine_specs.update(machine)
            machine_specs['password'] = password
            show_specs = """<fieldset>
<legend>VM Specs:</legend><ul class="no-bullets">
<li><input type="text" readonly value="%(os)s"> base system</li>
<li><input type="text" readonly value="%(flavor)s"> software flavor</li>
<li><input type="text" readonly value="%(memory)s"> MB memory</li>
<li><input type="text" readonly value="%(disk)s"> GB disk</li>
<li><input type="text" readonly value="%(cpu_count)s"> CPU's</li>
<li><input type="text" readonly value="%(vm_arch)s"> architecture</li>
"""
            if password != 'UNKNOWN':
                show_specs += """            
<li><input type="text" readonly value="%(password)s"> as VNC password</li>
"""
            show_specs += """            
</form></ul></fieldset>"""
            edit_specs = """<fieldset>
<legend>Edit VM Specs:</legend><ul class="no-bullets">
<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="action" value="edit">
""" % fill_helpers
            edit_specs += """
<input type="hidden" name="machine_name" value="%(name)s">
<input type="hidden" name="output_format" value="html">

<li><input type="text" readonly name="os" value="%(os)s"> base system</li>
<li><input type="text" readonly name="flavor" value="%(flavor)s"> software flavor</li>
<li><input type="text" readonly name="hypervisor_re" value="%(hypervisor_re)s"> hypervisor runtime env</li>
<li><input type="text" readonly name="sys_re" value="%(sys_re)s"> image pack runtime env</li>
<li><input type="text" name="memory" value="%(memory)s"> MB memory</li>
<li><input type="text" name="disk" value="%(disk)s"> GB disk</li>
<li><input type="text" name="cpu_count" value="%(cpu_count)s"> CPU's</li>
<li><select name="architecture">
"""
            for arch in [''] + configuration.architectures:
                select = ''
                if arch == machine_specs['architecture']:
                    select = 'selected'
                edit_specs += "<option %s value='%s'>%s</option>" % (
                    select, arch, arch)
            edit_specs += """</select> resource architecture
<li><input type="text" name="cpu_time" value="%(cpu_time)s"> s time slot</li>
<li><select name="vgrid" multiple>"""
            for vgrid_name in [any_vgrid] + \
                    user_vgrid_access(configuration, client_id):
                select = ''
                if vgrid_name in machine_specs['vgrid']:
                    select = 'selected'
                edit_specs += "<option %s>%s</option>" % (select, vgrid_name)
            edit_specs += """</select> %s(s)</li>""" % \
                          configuration.site_vgrid_label
            if password != 'UNKNOWN':
                edit_specs += """
<li><input type="text" readonly value="%(password)s"> as VNC password</li>
"""
            edit_specs += """
<input class="styled_button" type="submit" value="Save Changes">
</form>"""
            js_name = 'deletevm%s' % hexlify("%(name)s" % machine_specs)
            helper = html_post_helper(
                js_name, '%s.py' % target_op, {
                    'machine_name': machine_specs['name'],
                    'action': 'delete',
                    csrf_field: csrf_token
                })
            edit_specs += helper
            edit_specs += """<input class="styled_button" type="submit"
value="Delete Machine" onClick="javascript: confirmDialog(%s, '%s');" >
""" % (js_name, "Really permanently delete %(name)s VM?" % machine_specs)
            edit_specs += """</ul></fieldset>"""
            if machine['status'] == 'EXECUTING' and exec_time > boot_secs:
                machine_image = '<img src="/images/vms/' \
                    + machine_states[machine['status']] + '">'
            elif machine['status'] == 'EXECUTING' and exec_time < boot_secs:
                machine_image = \
                    '<img src="/images/vms/vm_desktop_loading.jpg' \
                    + '">'
            else:
                machine_image = '<img src="/images/vms/' \
                    + machine_states[machine['status']] + '">'
            machine_link = vms.machine_link(machine_image, machine['job_id'],
                                            machine['name'], machine['uuid'],
                                            machine['status'], machine_req)

            # Smack all the html together

            fill_dict = {}
            fill_dict.update(machine)
            fill_dict['link'] = machine_link
            fill_dict['show_specs'] = show_specs % machine_specs
            fill_dict['edit_specs'] = edit_specs % machine_specs
            pretty_machines += '''
<td style="vertical-align: top;">
<fieldset><legend>%(name)s</legend>
<div id="%(name)s-tabs" class="vm-tabs">
<ul>
<li><a href="#%(name)s-overview">Overview</a></li>
<li><a href="#%(name)s-edit">Advanced</a></li>
</ul>
<div id="%(name)s-overview">
<p>%(link)s</p>
%(show_specs)s
</div>
<div id="%(name)s-edit">
%(edit_specs)s
</div>
</div>
</fieldset>
</td>''' % fill_dict

        pretty_machines += '</tr></table>'

        output_objects.append({
            'object_type': 'html_form',
            'text': pretty_machines
        })
    else:
        output_objects.append({
            'object_type':
            'text',
            'text':
            "You don't have any virtual machines! "
            "Click 'Request Virtual Machine' to become a proud owner :)"
        })

    return (output_objects, status)
Ejemplo n.º 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, op_header=False)
    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,
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    show_description = accepted['description'][-1].lower() == 'true'

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

    template_path = os.path.join(base_dir, default_mrsl_filename)

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Submit Job'
    user_settings = title_entry.get('user_settings', {})
    output_objects.append({'object_type': 'header', 'text': 'Submit Job'})
    default_mrsl = get_default_mrsl(template_path)
    if not user_settings or not user_settings.has_key('SUBMITUI'):
        logger.info('Settings dict does not have SUBMITUI key - using default')
        submit_style = configuration.submitui[0]
    else:
        submit_style = user_settings['SUBMITUI']

    # We generate all 3 variants of job submission (fields, textarea, files),
    # initially hide them and allow to switch between them using js.

    # could instead extract valid prefixes as in settings.py
    # (means: by "eval" from configuration). We stick to hard-coding.
    submit_options = ['fields_form', 'textarea_form', 'files_form']

    open_button_id = 'open_fancy_upload'
    form_method = 'post'
    csrf_limit = get_csrf_limit(configuration)
    fill_helpers = {
        'dest_dir': '.' + os.sep,
        'fancy_open': open_button_id,
        'default_mrsl': default_mrsl,
        'form_method': form_method,
        'csrf_field': csrf_field,
        'csrf_limit': csrf_limit
    }
    target_op = 'uploadchunked'
    csrf_token = make_csrf_token(configuration, form_method, target_op,
                                 client_id, csrf_limit)
    (add_import, add_init, add_ready) = fancy_upload_js(configuration,
                                                        csrf_token=csrf_token)
    add_init += '''
    submit_options = %s;

    function setDisplay(this_id, new_d) {
        //console.log("setDisplay with: "+this_id);
        el = document.getElementById(this_id)
        if (el == undefined || el.style == undefined) {
            console.log("failed to locate display element: "+this_id);
            return; // avoid js null ref errors
        }
        el.style.display=new_d;
    }

    function switchTo(name) {
        //console.log("switchTo: "+name);
        for (o=0; o < submit_options.length; o++) {
            if (name == submit_options[o]) {
                setDisplay(submit_options[o],"block");
            } else {
                setDisplay(submit_options[o],"none");
            }
        }
    }
    ''' % submit_options
    add_ready += '''
    switchTo("%s");
    setUploadDest("%s");
    /* wrap openFancyUpload in function to avoid event data as argument */
    $("#%s").click(function() { openFancyUpload(); });
    ''' % (submit_style + "_form", fill_helpers['dest_dir'], open_button_id)
    fancy_dialog = fancy_upload_html(configuration)
    # TODO: can we update style inline to avoid explicit themed_styles?
    title_entry['style'] = themed_styles(
        configuration,
        base=['jquery.fileupload.css', 'jquery.fileupload-ui.css'],
        skin=['fileupload-ui.custom.css'],
        user_settings=user_settings)
    title_entry['script']['advanced'] += add_import
    title_entry['script']['init'] += add_init
    title_entry['script']['ready'] += add_ready

    output_objects.append({
        'object_type':
        'text',
        'text':
        'This page is used to submit jobs to the grid.'
    })

    output_objects.append({
        'object_type':
        'verbatim',
        'text':
        '''
There are %s interface styles available that you can choose among:''' %
        len(submit_options)
    })

    links = []
    for opt in submit_options:
        name = opt.split('_', 2)[0]
        links.append({
            'object_type': 'link',
            'destination': "javascript:switchTo('%s')" % opt,
            'class': 'submit%slink iconspace' % name,
            'title': 'Switch to %s submit interface' % name,
            'text': '%s style' % name,
        })
    output_objects.append({'object_type': 'multilinkline', 'links': links})

    output_objects.append({
        'object_type':
        'text',
        'text':
        '''
Please note that changes to the job description are *not* automatically
transferred if you switch style.'''
    })

    output_objects.append({
        'object_type':
        'html_form',
        'text':
        '<div id="fields_form" style="display:none;">\n'
    })

    # Fields
    output_objects.append({
        'object_type':
        'sectionheader',
        'text':
        'Please fill in your job description in the fields'
        ' below:'
    })
    output_objects.append({
        'object_type':
        'text',
        'text':
        """
Please fill in one or more fields below to define your job before hitting
Submit Job at the bottom of the page.
Empty fields will simply result in the default value being used and each field
is accompanied by a help link providing further details about the field."""
    })

    fill_helpers.update({'fancy_dialog': fancy_dialog})
    target_op = 'submitfields'
    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='submitjob'>
<form method='%(form_method)s' action='%(target_op)s.py'> 
<input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
""" % fill_helpers
    })
    show_fields = get_job_specs(configuration)
    try:
        parsed_mrsl = dict(parse_lines(default_mrsl))
    except:
        parsed_mrsl = {}

    # Find allowed VGrids and Runtimeenvironments and add them to
    # configuration object for automated choice handling

    vgrid_access = user_vgrid_access(configuration, client_id) + \
        [any_vgrid]
    vgrid_access.sort()
    configuration.vgrids = vgrid_access
    (re_status, allowed_run_envs) = list_runtime_environments(configuration)
    if not re_status:
        logger.error('Failed to extract allowed runtime envs: %s' %
                     allowed_run_envs)
        allowed_run_envs = []
    allowed_run_envs.sort()
    configuration.runtimeenvironments = allowed_run_envs
    # TODO: next call is slow because we walk and reload all pickles
    user_res = user_allowed_res_exes(configuration, client_id)

    # Add valid MAXFILL values to automated choice handling

    configuration.maxfills = [keyword_all] + maxfill_fields

    # Allow any exe unit on all allowed resources

    allowed_resources = ['%s_*' % res for res in user_res.keys()]
    allowed_resources.sort()
    configuration.resources = allowed_resources
    field_size = 30
    area_cols = 80
    area_rows = 5

    for (field, spec) in show_fields:
        title = spec['Title']
        if show_description:
            description = '%s<br />' % spec['Description']
        else:
            description = ''
        field_type = spec['Type']
        # Use saved value and fall back to default if it is missing
        saved = parsed_mrsl.get('::%s::' % field, None)
        if saved:
            if not spec['Type'].startswith('multiple'):
                default = saved[0]
            else:
                default = saved
        else:
            default = spec['Value']
        # Hide sandbox field if sandboxes are disabled
        if field == 'SANDBOX' and not configuration.site_enable_sandboxes:
            continue
        if 'invisible' == spec['Editor']:
            continue
        if 'custom' == spec['Editor']:
            continue
        output_objects.append({
            'object_type':
            'html_form',
            'text':
            """
<b>%s:</b>&nbsp;<a class='infolink iconspace' href='docs.py?show=job#%s'>
help</a><br />
%s""" % (title, field, description)
        })

        if 'input' == spec['Editor']:
            if field_type.startswith('multiple'):
                output_objects.append({
                    'object_type':
                    'html_form',
                    'text':
                    """
<textarea class='fillwidth padspace' name='%s' cols='%d' rows='%d'>%s</textarea><br />
""" % (field, area_cols, area_rows, '\n'.join(default))
                })
            elif field_type == 'int':
                output_objects.append({
                    'object_type':
                    'html_form',
                    'text':
                    """
<input type='number' name='%s' size='%d' value='%s' min=0 required pattern='[0-9]+' /><br />
""" % (field, field_size, default)
                })
            else:
                output_objects.append({
                    'object_type':
                    'html_form',
                    'text':
                    """
<input type='text' name='%s' size='%d' value='%s' /><br />
""" % (field, field_size, default)
                })
        elif 'select' == spec['Editor']:
            choices = available_choices(configuration, client_id, field, spec)
            res_value = default
            value_select = ''
            if field_type.startswith('multiple'):
                value_select += '<div class="scrollselect">'
                for name in choices:
                    # Blank default value does not make sense here
                    if not str(name):
                        continue
                    selected = ''
                    if str(name) in res_value:
                        selected = 'checked'
                    value_select += '''
                        <input type="checkbox" name="%s" %s value=%s>%s<br />
                        ''' % (field, selected, name, name)
                value_select += '</div>\n'
            else:
                value_select += "<select name='%s'>\n" % field
                for name in choices:
                    selected = ''
                    if str(res_value) == str(name):
                        selected = 'selected'
                    display = "%s" % name
                    if display == '':
                        display = ' '
                    value_select += """<option %s value='%s'>%s</option>\n""" \
                                    % (selected, name, display)
                value_select += """</select><br />\n"""
            output_objects.append({
                'object_type': 'html_form',
                'text': value_select
            })
        output_objects.append({'object_type': 'html_form', 'text': "<br />"})

    output_objects.append({
        'object_type':
        'html_form',
        'text':
        """
<br />
<table class='centertext'>
<tr><td><input type='submit' value='Submit Job' />
<input type='checkbox' name='save_as_default'> Save as default job template
</td></tr>
</table>
<br />
</form>
</div>
"""
    })
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        '''
</div><!-- fields_form-->
<div id="textarea_form" style="display:none;">
'''
    })

    # Textarea
    output_objects.append({
        'object_type':
        'sectionheader',
        'text':
        'Please enter your mRSL job description below:'
    })
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        """
<div class='smallcontent'>
Job descriptions can use a wide range of keywords to specify job requirements
and actions.<br />
Each keyword accepts one or more values of a particular type.<br />
The full list of keywords with their default values and format is available in
the on-demand <a href='docs.py?show=job'>mRSL Documentation</a>.
<p>
Actual examples for inspiration:
<a href=/public/cpuinfo.mRSL>CPU Info</a>,
<a href=/public/basic-io.mRSL>Basic I/O</a>,
<a href=/public/notification.mRSL>Job Notification</a>,
<a href=/public/povray.mRSL>Povray</a> and
<a href=/public/vcr.mRSL>VCR</a>
</div>
    """
    })

    target_op = 'textarea'
    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':
        """
<!-- 
Please note that textarea.py chokes if no nonempty KEYWORD_X_Y_Z fields 
are supplied: thus we simply send a bogus jobname which does nothing
-->
<form method='%(form_method)s' action='%(target_op)s.py'> 
<input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
<table class='submitjob'>
<tr><td class='centertext'>
<input type=hidden name=jobname_0_0_0 value=' ' />
<textarea class='fillwidth padspace' rows='25' name='mrsltextarea_0'>
%(default_mrsl)s
</textarea>
</td></tr>
<tr><td class='centertext'>
<input type='submit' value='Submit Job' />
<input type='checkbox' name='save_as_default' >Save as default job template
</td></tr>
</table>
</form>
""" % fill_helpers
    })

    output_objects.append({
        'object_type':
        'html_form',
        'text':
        '''
</div><!-- textarea_form-->
<div id="files_form" style="display:none;">
'''
    })
    # Upload form

    output_objects.append({
        'object_type':
        'sectionheader',
        'text':
        'Please upload your job file or packaged job files'
        ' below:'
    })
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        """
<form enctype='multipart/form-data' method='%(form_method)s'
    action='%(target_op)s.py'> 
<input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
<table class='files'>
<tr class='title'><td class='centertext' colspan=4>
Upload job files
</td></tr>
<tr><td colspan=3>
Upload file to current directory (%(dest_dir)s)
</td><td><br /></td></tr>
<tr><td colspan=2>
Extract package files (.zip, .tar.gz, .tar.bz2)
</td><td colspan=2>
<input type=checkbox name='extract_0' />
</td></tr>
<tr><td colspan=2>
Submit mRSL files (also .mRSL files included in packages)
</td><td colspan=2>
<input type=checkbox name='submitmrsl_0' checked />
</td></tr>
<tr><td>    
File to upload
</td><td class='righttext' colspan=3>
<input name='fileupload_0_0_0' type='file'/>
</td></tr>
<tr><td>
Optional remote filename (extra useful in windows)
</td><td class='righttext' colspan=3>
<input name='default_remotefilename_0' type='hidden' value='%(dest_dir)s'/>
<input name='remotefilename_0' type='text' size='50' value='%(dest_dir)s'/>
<input type='submit' value='Upload' name='sendfile'/>
</td></tr>
</table>
</form>
<table class='files'>
<tr class='title'><td class='centertext'>
Upload other files efficiently (using chunking).
</td></tr>
<tr><td class='centertext'>
<button id='%(fancy_open)s'>Open Upload dialog</button>
</td></tr>
</table>
</div><!-- files_form-->

%(fancy_dialog)s
""" % fill_helpers
    })

    return (output_objects, status)
Ejemplo n.º 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]
    (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)

    hosturl = accepted['hosturl'][-1]
    hostidentifier = accepted['hostidentifier'][-1]
    resource_id = '%s.%s' % (hosturl, hostidentifier)
    extra_selects = 3

    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)

    # Find allowed VGrids and Runtimeenvironments and add them to
    # configuration object for automated choice handling

    allowed_vgrids = [''] + res_vgrid_access(configuration, resource_id)
    allowed_vgrids.sort()

    configuration.vgrids = allowed_vgrids
    (re_status, allowed_run_envs) = list_runtime_environments(configuration)
    allowed_run_envs.sort()
    area_cols = 80
    area_rows = 5

    status = returnvalues.OK

    logger.info('Starting Resource edit GUI.')

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Resource Editor'
    output_objects.append({'object_type': 'header', 'text': 'Resource Editor'})
    output_objects.append({
        'object_type':
        'sectionheader',
        'text':
        '%s Resource Editor' % configuration.short_title
    })
    output_objects.append({
        'object_type':
        'text',
        'text':
        '''
Please fill in or edit the fields below to fit your %s resource reservation. Most fields
will work with their default values. So if you are still in doubt after reading the help
description, you can likely just leave the field alone.''' %
        configuration.short_title
    })

    if hosturl and hostidentifier:
        conf = init_conf(configuration, hosturl, hostidentifier)
        if not conf:
            status = returnvalues.CLIENT_ERROR
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '''No such resource! (%s.%s)''' % (hosturl, hostidentifier)
            })
            return (output_objects, status)
    else:
        conf = empty_resource_config(configuration)

    res_fields = resconfkeywords.get_resource_specs(configuration)
    exe_fields = resconfkeywords.get_exenode_specs(configuration)
    store_fields = resconfkeywords.get_storenode_specs(configuration)

    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 = 'reseditaction'
    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' />
""" % fill_helpers
    })

    # Resource overall fields

    output_objects.append({
        'object_type': 'sectionheader',
        'text': "Main Resource Settings"
    })
    output_objects.append({
        'object_type':
        'text',
        'text':
        """This section configures general options for the resource."""
    })

    (title, field) = ('Host FQDN', 'HOSTURL')
    if hosturl:
        try:
            hostip = conf.get('HOSTIP', socket.gethostbyname(hosturl))
        except:
            hostip = '<unknown>'
        output_objects.append({
            'object_type':
            'html_form',
            'text':
            """<br />
<b>%s:</b>&nbsp;<a class='infolink iconspace' href='resedithelp.py#res-%s'>help</a><br />
<input type='hidden' name='%s' value='%s' />
<input type='hidden' name='hostip' value='%s' />
%s
<br />
<br />""" % (title, field, field, conf[field], hostip, conf[field])
        })
    else:
        output_objects.append({
            'object_type':
            'html_form',
            'text':
            """<br />
<b>%s:</b>&nbsp;<a class='infolink iconspace' href='resedithelp.py#res-%s'>help</a><br />
<input class='fillwidth padspace' type='text' name='%s' size='%d' value='%s'
    required pattern='[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+'
    title='Fully qualified domain name or Internet IP address of the resource'
/>
<br />
<br />""" % (title, field, field, field_size(conf[field]), conf[field])
        })

    (title, field) = ('Host identifier', 'HOSTIDENTIFIER')
    if hostidentifier:
        output_objects.append({
            'object_type':
            'html_form',
            'text':
            """<br />
<b>%s:</b>&nbsp;<a class='infolink iconspace' href='resedithelp.py#res-%s'>help</a><br />
<input type='hidden' name='%s' value='%s' />
%s
<br />
<br />""" % (title, field, field, conf[field], conf[field])
        })

    (field, title) = 'frontendhome', 'Frontend Home Path'
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        """<br />
<b>%s:</b>&nbsp;<a class='infolink iconspace' href='resedithelp.py#%s'>help</a><br />
<input class='fillwidth padspace' type='text' name='%s' size='%d' value='%s'
    required pattern='[^ ]+' title='Absolute path to user home on the resource'
/>
<br />
<br />""" % (title, field, field, field_size(conf[field]), conf[field])
    })

    for (field, spec) in res_fields:
        title = spec['Title']
        field_type = spec['Type']
        if spec['Required']:
            required_str = 'required'
        else:
            required_str = ''

        if 'invisible' == spec['Editor']:
            continue
        elif 'input' == spec['Editor']:
            if spec['Type'] == 'int':
                input_str = """
<input class='fillwidth padspace' type='number' name='%s' size='%d' value='%s'
    min=0 pattern='[0-9]+' %s />
""" % (field, field_size(conf[field]), conf[field], required_str)
            else:
                input_str = """
<input class='fillwidth padspace' type='text' name='%s' size='%d' value='%s'
    %s />
""" % (field, field_size(conf[field]), conf[field], required_str)
            output_objects.append({
                'object_type':
                'html_form',
                'text':
                """<br />
<b>%s:</b>&nbsp;<a class='infolink iconspace' href='resedithelp.py#res-%s'>help</a>
<br />
%s<br />
<br />""" % (title, field, input_str)
            })
        elif 'select' == spec['Editor']:
            choices = available_choices(configuration, client_id, resource_id,
                                        field, spec)
            res_value = conf[field]
            value_select = ''
            if field_type.startswith('multiple'):
                select_count = len(res_value) + extra_selects
            else:
                select_count = 1
                res_value = [res_value]
            for i in range(select_count):
                value_select += "<select name='%s'>\n" % field
                for name in choices:
                    selected = ''
                    if i < len(res_value) and res_value[i] == name:
                        selected = 'selected'
                    display = "%s" % name
                    if display == '':
                        display = ' '
                    value_select += """<option %s value='%s'>%s</option>\n""" \
                                    % (selected, name, display)
                value_select += """</select><br />\n"""
            output_objects.append({
                'object_type':
                'html_form',
                'text':
                """<br />
<b>%s:</b>&nbsp;<a class='infolink iconspace' href='resedithelp.py#res-%s'>help</a><br />
%s
<br />""" % (title, field, value_select)
            })

    # Not all resource fields here map directly to keywords/specs input field

    (title, field) = ('Runtime Environments', 'RUNTIMEENVIRONMENT')
    re_list = conf[field]
    show = re_list + [('', []) for i in range(extra_selects)]
    re_select = "<input type='hidden' name='runtime_env_fields' value='%s'/>\n" \
                % len(show)
    i = 0
    for active in show:
        re_select += "<select name='runtimeenvironment%d'>\n" % i
        for name in allowed_run_envs + ['']:
            selected = ''
            if active[0] == name:
                selected = 'selected'
            display = "%s" % name
            if display == '':
                display = ' '
            re_select += """<option %s value='%s'>%s</option>\n""" % \
                         (selected, name, display)
        re_select += """</select><br />\n"""
        values = '\n'.join(['%s=%s' % pair for pair in active[1]])
        re_select += "<textarea class='fillwidth padspace' cols='%d' rows='%d' name='re_values%d'>%s</textarea><br />\n" % \
                     (area_cols, area_rows, i, values)
        i += 1

    output_objects.append({
        'object_type':
        'html_form',
        'text':
        """<br />
<b>%s:</b>&nbsp;<a class='infolink iconspace' href='resedithelp.py#res-%s'>help</a><br />
Please enter any required environment variable settings on the form NAME=VALUE in the box below
each selected runtimeenvironment.<br />
%s
<br />""" % (title, field, re_select)
    })

    # Execution node fields

    output_objects.append({
        'object_type': 'sectionheader',
        'text': "Execution nodes"
    })
    output_objects.append({
        'object_type':
        'text',
        'text':
        """This section configures execution nodes on the resource."""
    })
    (field, title) = 'executionnodes', 'Execution Node(s)'
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        """<br />
<b>%s:</b>&nbsp;<a class='infolink iconspace' href='resedithelp.py#exe-%s'>help</a><br />
<input class='fillwidth padspace' type='text' name='exe-%s' size='%d' value='%s' />
<br />
<br />""" % (title, field, field, field_size(
            conf['all_exes'][field]), conf['all_exes'][field])
    })

    (field, title) = 'executionhome', 'Execution Home Path'
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        """<br />
<b>%s:</b>&nbsp;<a class='infolink iconspace' href='resedithelp.py#exe-%s'>help</a><br />
<input class='fillwidth padspace' type='text' name='exe-%s' size='%d' value='%s' />
<br />
<br />""" % (title, field, field, field_size(
            conf['all_exes'][field]), conf['all_exes'][field])
    })

    for (field, spec) in exe_fields:
        title = spec['Title']
        field_type = spec['Type']
        # We don't really have a good map of required fields here so disable
        required_str = ''
        if 'invisible' == spec['Editor']:
            continue
        elif 'input' == spec['Editor']:
            if spec['Type'] == 'int':
                input_str = """
<input class='fillwidth padspace' type='number' name='exe-%s' size='%d' value='%s'
    min=0 pattern='[0-9]+' %s />
""" % (field, field_size(conf['all_exes'][field]), conf['all_exes'][field],
                required_str)
            else:
                input_str = """
<input class='fillwidth padspace' type='text' name='exe-%s' size='%d' value='%s'
    %s />
""" % (field, field_size(conf['all_exes'][field]), conf['all_exes'][field],
                required_str)

            output_objects.append({
                'object_type':
                'html_form',
                'text':
                """<br />
<b>%s:</b>&nbsp;<a class='infolink iconspace' href='resedithelp.py#exe-%s'>help</a>
<br />
%s
<br />
<br />""" % (title, field, input_str)
            })
        elif 'select' == spec['Editor']:
            choices = available_choices(configuration, client_id, resource_id,
                                        field, spec)
            exe_value = conf['all_exes'][field]
            value_select = ''
            if field_type.startswith('multiple'):
                select_count = len(exe_value) + extra_selects
            else:
                select_count = 1
                exe_value = [exe_value]
            for i in range(select_count):
                value_select += "<select name='exe-%s'>\n" % field
                for name in choices:
                    selected = ''
                    if i < len(exe_value) and exe_value[i] == name:
                        selected = 'selected'
                    display = "%s" % name
                    if display == '':
                        display = ' '
                    value_select += """<option %s value='%s'>%s</option>\n""" \
                                    % (selected, name, display)
                value_select += """</select><br />\n"""
            output_objects.append({
                'object_type':
                'html_form',
                'text':
                """<br />
<b>%s:</b>&nbsp;<a class='infolink iconspace' href='resedithelp.py#exe-%s'>help</a><br />
%s
<br />""" % (title, field, value_select)
            })

    # Storage node fields

    output_objects.append({
        'object_type': 'sectionheader',
        'text': "Storage nodes"
    })
    output_objects.append({
        'object_type':
        'text',
        'text':
        """This section configures storage nodes on the resource."""
    })

    (field, title) = 'storagenodes', 'Storage Node(s)'
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        """<br />
<b>%s:</b>&nbsp;<a class='infolink iconspace' href='resedithelp.py#store-%s'>help</a><br />
<input class='fillwidth padspace' type='text' name='store-%s' size='%d' value='%s' />
<br />
<br />""" % (title, field, field, field_size(
            conf['all_stores'][field]), conf['all_stores'][field])
    })

    (field, title) = 'storagehome', 'Storage Home Path'
    output_objects.append({
        'object_type':
        'html_form',
        'text':
        """<br />
<b>%s:</b>&nbsp;<a class='infolink iconspace' href='resedithelp.py#store-%s'>help</a><br />
<input class='fillwidth padspace' type='text' name='store-%s' size='%d' value='%s' />
<br />
<br />""" % (title, field, field, field_size(
            conf['all_stores'][field]), conf['all_stores'][field])
    })

    for (field, spec) in store_fields:
        title = spec['Title']
        field_type = spec['Type']
        # We don't really have a good map of required fields here so disable
        required_str = ''
        if 'invisible' == spec['Editor']:
            continue
        elif 'input' == spec['Editor']:
            if spec['Type'] == 'int':
                input_str = """
<input class='fillwidth padspace' type='number' name='store-%s' size='%d' value='%s'
    min=0 pattern='[0-9]+' %s />
""" % (field, field_size(conf['all_stores'][field]), conf['all_stores'][field],
                required_str)
            else:
                input_str = """
<input class='fillwidth padspace' type='text' name='store-%s' size='%d' value='%s'
    %s />
""" % (field, field_size(conf['all_stores'][field]), conf['all_stores'][field],
                required_str)

            output_objects.append({
                'object_type':
                'html_form',
                'text':
                """<br />
<b>%s:</b>&nbsp;<a class='infolink iconspace' href='resedithelp.py#store-%s'>help</a>
<br />
%s
<br />
<br />""" % (title, field, input_str)
            })
        elif 'select' == spec['Editor']:
            choices = available_choices(configuration, client_id, resource_id,
                                        field, spec)
            store_value = conf['all_stores'][field]
            value_select = ''
            if field_type.startswith('multiple'):
                select_count = len(store_value) + extra_selects
            else:
                select_count = 1
                store_value = [store_value]
            for i in range(select_count):
                value_select += "<select name='store-%s'>\n" % field
                for name in choices:
                    selected = ''
                    if i < len(store_value) and store_value[i] == name:
                        selected = 'selected'
                    display = "%s" % name
                    if display == '':
                        display = ' '
                    value_select += """<option %s value='%s'>%s</option>\n""" \
                                    % (selected, name, display)
                value_select += """</select><br />\n"""
            output_objects.append({
                'object_type':
                'html_form',
                'text':
                """<br />
<b>%s:</b>&nbsp;<a class='infolink iconspace' href='resedithelp.py#store-%s'>help</a><br />
%s
<br />""" % (title, field, value_select)
            })

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

    return (output_objects, status)
Ejemplo n.º 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, 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': 'Send request'})
    (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': 'warning', 'text':
                           '''Remember that sending a membership or ownership
request generates a message to the owners of the target. All requests are 
logged together with the ID of the submitter. Spamming and other abuse will
not be tolerated!'''})

    output_objects.append({'object_type': 'sectionheader', 'text':
                           'Request %s membership/ownership' % label})

    form_method = 'post'
    csrf_limit = get_csrf_limit(configuration)
    fill_helpers =  {'vgrid_label': label,
                     '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' />
<table align='center'>
<tr><td>Request type</td><td><select name=request_type>
<option value=vgridmember>%(vgrid_label)s membership</option>
<option value=vgridowner>%(vgrid_label)s ownership</option>
</select></td></tr>
<tr><td>
%(vgrid_label)s name </td><td><input name=vgrid_name />
</td></tr>
<tr>
<td>Reason (text to owners)</td><td><input name=request_text size=40 /></td>
</tr>
<tr><td><input type='submit' value='Submit' /></td><td></td></tr></table>
</form>""" % fill_helpers})

    output_objects.append({'object_type': 'sectionheader', 'text':
                           'Request resource ownership'})
    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' />
<table align='center'>
<tr><td>Request type</td><td><select name=request_type>
<option value=resourceowner>Resource ownership</option>
</select></td></tr>
<tr><td>
Resource ID </td><td><input name=unique_resource_name />
</td></tr>
<tr>
<td>Reason (text to owners)</td><td><input name=request_text size=40 /></td>
</tr>
<tr><td><input type='submit' value='Submit' /></td><td></td></tr></table>
</form>""" % fill_helpers})

    output_objects.append({'object_type': 'sectionheader', 'text':
                           'Send message'})
    protocol_options = ''
    for proto in [any_protocol] + configuration.notify_protocols:
        protocol_options += '<option value=%s>%s</option>\n' % (proto, proto)
    fill_helpers['protocol_options'] = protocol_options
    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' />
<table align='center'>
<tr><td>Request type</td><td><select name=request_type>
<option value=plain>Plain message</option>
</select></td></tr>
<tr><td>
User ID </td><td><input name=cert_id size=50 />
</td></tr>
<tr><td>Protocol</td><td><select name=protocol>
%(protocol_options)s
</select></td></tr>
<tr>
<td>Message</td>
<td><textarea name=request_text cols=72 rows=10 /></textarea></td>
</tr>
<tr><td><input type='submit' value='Send' /></td><td></td></tr></table>
</form>""" % fill_helpers})

    return (output_objects, returnvalues.OK)
Ejemplo n.º 31
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)

    flags = ''.join(accepted['flags'])
    lines = int(accepted['lines'][-1])
    pattern_list = accepted['path']

    # 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)})

    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:
            real_path = os.path.abspath(server_path)
            if not valid_user_path(real_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, real_path, pattern))
                continue
            match.append(real_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 real_path in match:
            relative_path = real_path.replace(base_dir, '')
            output_lines = []
            try:
                filedes = open(real_path, 'r')
                i = 0
                for line in filedes:
                    if i >= lines:
                        break
                    output_lines.append(line)
                    i += 1
                filedes.close()
            except Exception, 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)
Ejemplo n.º 32
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)

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

    status = returnvalues.OK
    user_map = get_full_user_map(configuration)
    user_dict = user_map.get(client_id, None)
    # Optional limitation of cloud access permission
    if not user_dict or not cloud_access_allowed(configuration, user_dict):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            "You don't have permission to access the cloud facilities on "
            "this site"
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    services = configuration.cloud_services

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

    add_init += '''
    function get_instance_id() {
        console.log("in get_instance_id");
        console.log("found val: "+$("#select-instance-id").val());
        return $("#select-instance-id").val();
    }
    function get_instance_label() {
        console.log("in get_instance_label");
        console.log("found val: "+$("#select-instance-id > option:selected").text());
        return $("#select-instance-id > option:selected").text();
    }
    '''
    add_ready += '''
        /* NOTE: requires managers CSS fix for proper tab bar height */
        $(".cloud-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': 'html_form',
        'text': man_base_html(configuration)
    })

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

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

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

    form_method = 'post'
    csrf_limit = get_csrf_limit(configuration)
    fill_helpers = {
        'site': configuration.short_title,
        'form_method': form_method,
        'csrf_field': csrf_field,
        'csrf_limit': csrf_limit
    }
    target_op = 'reqcloudservice'
    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})

    action_list = [
        ('start', 'Start'),
        ('stop', 'Stop'),
        ('softrestart', 'Soft boot'),
        ('hardrestart', 'Hard boot'),
        ('status', 'Status'),
        # NOTE: expose console on status page
        #('webaccess', 'Console'),
        ('updatekeys', 'Set keys on'),
        ('create', 'Create'),
        ('delete', 'Delete')
    ]
    # Delete instance form helper shared for all cloud services
    helper = html_post_helper(
        "%s" % target_op, '%s.py' % target_op, {
            'instance_id': '__DYNAMIC__',
            'service': '__DYNAMIC__',
            'action': 'delete',
            csrf_field: csrf_token
        })
    output_objects.append({'object_type': 'html_form', 'text': helper})

    for service in services:
        logger.debug("service: %s" % service)
        cloud_id = service['service_name']
        cloud_title = service['service_title']
        rules_of_conduct = service['service_rules_of_conduct']
        cloud_flavor = service.get("service_provider_flavor", "openstack")

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

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

        if not check_cloud_available(configuration, client_id, cloud_id,
                                     cloud_flavor):
            logger.error("Failed to connect to cloud: %s" % cloud_id)
            output_objects.append(
                {'object_type': 'error_text', 'text':
                 'The %s cloud service is currently unavailable' % \
                 cloud_title})
            output_objects.append({
                'object_type': 'html_form',
                'text': '''
        </div>
            '''
            })
            status = returnvalues.SYSTEM_ERROR
            continue

        # Lookup user-specific allowed images (colon-separated image names)
        allowed_images = allowed_cloud_images(configuration, client_id,
                                              cloud_id, cloud_flavor)
        if not allowed_images:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "No valid instance images for %s" % cloud_title
            })
            output_objects.append({
                'object_type': 'html_form',
                'text': '''
        </div>
            '''
            })
            continue

        fill_helpers.update({
            'cloud_id': cloud_id,
            'cloud_title': cloud_title,
            'target_op': target_op,
            'rules_of_conduct': rules_of_conduct
        })

        delete_html = ""
        # Manage existing instances
        saved_instances = cloud_load_instance(configuration, client_id,
                                              cloud_id, keyword_all)

        saved_fields = ['INSTANCE_IMAGE']
        instance_fields = ['public_fqdn', 'status']
        status_map = status_all_cloud_instances(configuration, client_id,
                                                cloud_id, cloud_flavor,
                                                saved_instances.keys(),
                                                instance_fields)

        # TODO: halfwidth styling does not really work on select elements
        delete_html += """
    <div class='cloud-instance-delete fillwidth'>
        <h3>Permanently delete a %(cloud_title)s cloud instance</h3>
        <form class='delete-cloud-instance' target='#'>
            <p class='cloud-instance-input fillwidth'>
            <label class='fieldlabel halfwidth'>Instance</label>
            <span class='halfwidth'>
            <select id='select-instance-id'
            class='styled-select html-select halfwidth padspace'
            name='instance_id'>
        """ % fill_helpers

        output_objects.append({
            'object_type':
            'html_form',
            'text':
            """
        <div class='cloud-management fillwidth'>
        <h3>Manage %(cloud_title)s instances</h3>
        <br/>
        <div class='cloud-instance-grid'>
        <div class='cloud-instance-grid-left'>
        <label class='fieldlabel fieldheader'>Name</label>
        </div>
        <div class='cloud-instance-grid-middle'>
        <label class='fieldlabel fieldheader'>Instance Details</label>
        </div>
        <div class='cloud-instance-grid-right'>
        <label class='fieldlabel fieldheader'>Actions</label>
        </div>
            """ % fill_helpers
        })
        for (instance_id, instance_dict) in saved_instances.items():
            instance_label = instance_dict.get('INSTANCE_LABEL', instance_id)
            logger.debug("Management entries for %s %s cloud instance %s" %
                         (client_id, cloud_id, instance_id))
            instance_html = """
        <div class='cloud-instance-grid-left'>
        <label class='fieldlabel'>%s</label>
        </div>
        <div class='cloud-instance-grid-middle'>
            """ % instance_label
            for field in saved_fields:
                field_val = saved_instances[instance_id].get(field, "-")
                if field == 'INSTANCE_IMAGE':
                    for (img_name, _, img_alias) in allowed_images:
                        if img_name == field_val:
                            field_val = img_alias
                instance_html += """
            <span class='fieldstatus entry leftpad'>%s</span>
            """ % field_val
            for field in instance_fields:
                field_val = status_map[instance_id].get(field, "-")
                instance_html += """
            <span class='fieldstatus entry leftpad'>%s</span>
            """ % field_val
            instance_html += """
        </div>
        <div class='cloud-instance-grid-right'>
            """
            output_objects.append({
                'object_type': 'html_form',
                'text': instance_html
            })
            for (action, title) in action_list:
                if action in cloud_edit_actions:
                    continue
                query = 'action=%s;service=%s;instance_id=%s' % \
                        (action, cloud_id, instance_id)
                url = 'reqcloudservice.py?%s' % query
                #output_service = {
                #    'object_type': 'service',
                #    'name': "%s" % title,
                #    'targetlink': url
                #}
                #output_objects.append(output_service)
                output_objects.append({
                    'object_type':
                    'link',
                    'destination':
                    url,
                    'text':
                    title,
                    'class':
                    'ui-button',
                    'title':
                    '%s %s' % (title, instance_label)
                })

            output_objects.append({
                'object_type':
                'html_form',
                'text':
                """
        </div>        
            """
            })
            delete_html += """<option value='%s'>%s</option>
            """ % (instance_id, instance_label)

        output_objects.append({
            'object_type':
            'html_form',
            'text':
            """
        </div>
        </div>
        """
        })

        delete_html += """
            </select>
            </span>
            </p>
            <p class='fillwidth'>
            <input type='submit' value='Delete Instance' onClick='javascript:confirmDialog(%(target_op)s, \"Really permanently delete your %(cloud_title)s \"+get_instance_label()+\" instance including all local data?\", undefined, {instance_id: get_instance_id(), service: \"%(cloud_id)s\"}); return false;' />
            </p>
        </form>
    </div>
        """ % fill_helpers

        # Create new instance
        create_html = """
    <div class='cloud-instance-create fillwidth'>
        <h3>Create a new %(cloud_title)s cloud instance</h3>
        <form class='create_cloud_instance' method='%(form_method)s' action='%(target_op)s.py'>
            <input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
            <input type='hidden' name='service' value='%(cloud_id)s' />
            <input type='hidden' name='action' value='create' />
            <p class='cloud-instance-input fillwidth'>
            <label class='fieldlabel halfwidth'>Label</label>
            <span class='halfwidth'>
            <input class='halfwidth padspace' type='text' name='instance_label' value='' />
            </span>
            </p>
            <p class='cloud-instance-input fillwidth'>
            <label class='fieldlabel halfwidth'>Image</label>
            <span class='halfwidth'>
            <select class='styled-select html-select halfwidth padspace' name='instance_image'>
            """
        for (image_name, _, image_alias) in allowed_images:
            create_html += """<option value='%s'>%s</option>
            """ % (image_name, image_alias)
        create_html += """
            </select>
            </span>
            </p>
            <p class='cloud-instance-input fillwidth'>
            <label class='fieldlabel halfwidth'>
            Accept <a href='%(rules_of_conduct)s'>Cloud Rules of Conduct</a>
            </label>
            <span class='halfwidth'>
            <label class='switch'>
            <input type='checkbox' mandatory name='accept_terms'>
            <span class='slider round'></span></label>
            </span>
            </p>
            <p class='fillwidth'>
            <input type='submit' value='Create Instance' />
            </p>
        </form>
    </div>    
        """
        output_objects.append({
            'object_type': 'html_form',
            'text': create_html % fill_helpers
        })

        if saved_instances:
            output_objects.append({
                'object_type': 'html_form',
                'text': delete_html % fill_helpers
            })

        output_objects.append({
            'object_type': 'html_form',
            'text': '''
        </div>
            '''
        })

    output_objects.append({
        'object_type': 'html_form',
        'text': '''
    </div>
    '''
    })

    return (output_objects, status)
Ejemplo n.º 33
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)

    flags = ''.join(accepted['flags'])
    dst = accepted['dst'][-1].lstrip(os.sep)
    pattern_list = accepted['src']
    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]
    dst = os.path.join(current_dir, 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)

    # 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

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'ZIP/TAR archiver'
    output_objects.append(
        {'object_type': 'header', 'text': 'ZIP/TAR archiver'})

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

    if 'h' in flags:
        usage(output_objects)

    # 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):

        # out of bounds

        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)

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

    # 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('pack 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):

        # out of bounds

        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 os.path.isdir(os.path.dirname(abs_dest)):
        output_objects.append({'object_type': 'error_text', 'text':
                               "No such destination directory: %s"
                               % os.path.dirname(relative_dest)})
        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 pack to "%s": inside a read-only location!' %
             relative_dest})
        return (output_objects, returnvalues.CLIENT_ERROR)

    status = returnvalues.OK

    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:
            logger.debug("%s: inspecting: %s" % (op_name, server_path))
            # 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
            else:
                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: cannot pack '%s': no valid src paths"
                                   % (op_name, pattern)})
            status = returnvalues.CLIENT_ERROR
            continue

        for abs_path in match:
            relative_path = abs_path.replace(base_dir, '')
            # Generally refuse handling symlinks including root vgrid shares
            if os.path.islink(abs_path):
                output_objects.append(
                    {'object_type': 'warning', 'text': """You're not allowed to
pack entire special folders like %s shared folders!""" %
                     configuration.site_vgrid_label})
                status = returnvalues.CLIENT_ERROR
                continue
            # Additionally refuse operations on inherited subvgrid share roots
            elif in_vgrid_share(configuration, abs_path) == relative_path:
                output_objects.append(
                    {'object_type': 'warning', 'text': """You're not allowed to
pack entire %s shared folders!""" % configuration.site_vgrid_label})
                status = returnvalues.CLIENT_ERROR
                continue
            elif os.path.realpath(abs_path) == os.path.realpath(base_dir):
                logger.error("%s: refusing pack home dir: %s" % (op_name,
                                                                 abs_path))
                output_objects.append(
                    {'object_type': 'warning', 'text':
                     "You're not allowed to pack your entire home directory!"
                     })
                status = returnvalues.CLIENT_ERROR
                continue
            elif os.path.realpath(abs_dest) == os.path.realpath(abs_path):
                output_objects.append({'object_type': 'warning', 'text':
                                       'overlap in source and destination %s'
                                       % relative_dest})
                status = returnvalues.CLIENT_ERROR
                continue
            if verbose(flags):
                output_objects.append(
                    {'object_type': 'file', 'name': relative_path})

            (pack_status, msg) = pack_archive(configuration, client_id,
                                              relative_path, relative_dest)
            if not pack_status:
                output_objects.append({'object_type': 'error_text',
                                       'text': 'Error: %s' % msg})
                status = returnvalues.CLIENT_ERROR
                continue
            output_objects.append({'object_type': 'text', 'text':
                                   'Added %s to %s' % (relative_path,
                                                       relative_dest)})

    output_objects.append({'object_type': 'text', 'text':
                           'Packed archive of %s is now available in %s'
                           % (', '.join(pattern_list), relative_dest)})
    output_objects.append({'object_type': 'link', 'text': 'Download archive',
                           'destination': os.path.join('..', client_dir,
                                                       relative_dest)})

    return (output_objects, status)
Ejemplo n.º 34
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'

    # 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).removeAttr("checked");
        $(dir_id).removeAttr("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").removeAttr("checked");
        $("#userpassword_choice").removeAttr("checked");
        $("#userkey_choice").removeAttr("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)
Ejemplo n.º 35
0
def main(client_id, user_arguments_dict, environ=None):
    """Main function used by front end"""

    if environ is None:
        environ = os.environ

    (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)

    flags = ''.join(accepted['flags'])
    src_list = accepted['src']
    dst = accepted['dst'][-1]
    iosessionid = accepted['iosessionid'][-1]
    share_id = accepted['share_id'][-1]
    freeze_id = accepted['freeze_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)

    # 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

    # Special handling if used from a job (no client_id but iosessionid)
    if not client_id and iosessionid:
        base_dir = os.path.realpath(configuration.webserver_home
                                    + os.sep + iosessionid) + os.sep

    # Use selected base as source and destination dir by default
    src_base = dst_base = base_dir

    # Sharelink import if share_id is given - change to sharelink as src base
    if share_id:
        try:
            (share_mode, _) = extract_mode_id(configuration, share_id)
        except ValueError, err:
            logger.error('%s called with invalid share_id %s: %s' %
                         (op_name, share_id, err))
            output_objects.append(
                {'object_type': 'error_text', 'text':
                 'Invalid sharelink ID: %s' % share_id})
            return (output_objects, returnvalues.CLIENT_ERROR)
        # TODO: load and check sharelink pickle (currently requires client_id)
        if share_mode == 'write-only':
            logger.error('%s called import from write-only sharelink: %s'
                         % (op_name, accepted))
            output_objects.append(
                {'object_type': 'error_text', 'text':
                 'Sharelink %s is write-only!' % share_id})
            return (output_objects, returnvalues.CLIENT_ERROR)
        target_dir = os.path.join(share_mode, share_id)
        src_base = os.path.abspath(os.path.join(configuration.sharelink_home,
                                                target_dir)) + os.sep
        if os.path.isfile(os.path.realpath(src_base)):
            logger.error('%s called import on single file sharelink: %s'
                         % (op_name, share_id))
            output_objects.append(
                {'object_type': 'error_text', 'text': """Import is only
supported for directory sharelinks!"""})
            return (output_objects, returnvalues.CLIENT_ERROR)
        elif not os.path.isdir(src_base):
            logger.error('%s called import with non-existant sharelink: %s'
                         % (client_id, share_id))
            output_objects.append(
                {'object_type': 'error_text', 'text': 'No such sharelink: %s'
                 % share_id})
            return (output_objects, returnvalues.CLIENT_ERROR)
Ejemplo n.º 36
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')
    title_entry['text'] = 'Runtime Environments'
    (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)

    operation = accepted['operation'][-1]

    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))
    if operation in show_operations:

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

        refresh_call = 'ajax_redb()'
        table_spec = {
            'table_id': 'runtimeenvtable',
            'sort_order': '[[2,1],[0,0]]',
            'refresh_call': refresh_call
        }
        (add_import, add_init,
         add_ready) = man_base_js(configuration, [table_spec])
        if operation == "show":
            add_ready += '%s;' % refresh_call
        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': 'Runtime Environments'
        })

        output_objects.append({
            'object_type':
            'text',
            'text':
            'Runtime environments specify software/data available on resources.'
        })
        output_objects.append({
            'object_type': 'link',
            'destination': 'docs.py?show=Runtime+Environments',
            'class': 'infolink iconspace',
            'title': 'Show information about runtime environment',
            'text': 'Documentation on runtime environments'
        })

        output_objects.append({
            'object_type': 'sectionheader',
            'text': 'Existing runtime environments'
        })

        # Helper form for removes

        form_method = 'post'
        csrf_limit = get_csrf_limit(configuration)
        target_op = 'deletere'
        csrf_token = make_csrf_token(configuration, form_method, target_op,
                                     client_id, csrf_limit)
        helper = html_post_helper('delre', '%s.py' % target_op, {
            're_name': '__DYNAMIC__',
            csrf_field: csrf_token
        })
        output_objects.append({'object_type': 'html_form', 'text': helper})

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

    runtimeenvironments = []
    if operation in list_operations:
        re_map = get_re_map(configuration)
        provider_map = get_re_provider_map(configuration)

        for (re_name, cache_dict) in re_map.items():
            re_dict = cache_dict[CONF]
            # Set providers explicitly after build_reitem_object to avoid import loop
            re_item = build_reitem_object(configuration, re_dict)
            re_name = re_item['name']
            re_item['providers'] = provider_map.get(re_name, [])
            re_item['resource_count'] = len(re_item['providers'])

            re_item['viewruntimeenvlink'] = {
                'object_type': 'link',
                'destination': "showre.py?re_name=%s" % re_name,
                'class': 'infolink iconspace',
                'title': 'View %s runtime environment' % re_name,
                'text': ''
            }
            if client_id == re_item['creator']:
                re_item['ownerlink'] = {
                    'object_type':
                    'link',
                    'destination':
                    "javascript: confirmDialog(%s, '%s', %s, %s);" %
                    ('delre', 'Really delete %s?' % re_name, 'undefined',
                     "{re_name: '%s'}" % re_name),
                    'class':
                    'removelink iconspace',
                    'title':
                    'Delete %s runtime environment' % re_name,
                    'text':
                    ''
                }
            runtimeenvironments.append(re_item)

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

    if operation in show_operations:
        if configuration.site_swrepo_url:
            output_objects.append({
                'object_type': 'sectionheader',
                'text': 'Software Packages'
            })
            output_objects.append({
                'object_type':
                'link',
                'destination':
                configuration.site_swrepo_url,
                'class':
                'swrepolink iconspace',
                'title':
                'Browse available software packages',
                'text':
                'Open software catalogue for %s' % configuration.short_title,
            })

        output_objects.append({
            'object_type': 'sectionheader',
            'text': 'Additional Runtime Environments'
        })
        output_objects.append({
            'object_type': 'link',
            'destination': 'adminre.py',
            'class': 'addlink iconspace',
            'title': 'Specify a new runtime environment',
            'text': 'Create a new runtime environment'
        })

    logger.info("%s %s end for %s" % (op_name, operation, client_id))
    return (output_objects, returnvalues.OK)
Ejemplo n.º 37
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)

    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.info("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

    output_objects.append({
        'object_type': 'header',
        'class': 'fileman-title',
        'container_class': 'fillwidth',
        '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)
Ejemplo n.º 38
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]

    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)

    # IMPORTANT: we can not validate input completely here!
    # We validate the parts used in the path manipulation and only use
    # the remaining variables directly in the generated config file that
    # is then handed to the parser for full validation.

    critical_arguments = {}
    critical_fields = defaults.keys()
    # IMPORTANT: we must explicitly inlude CSRF token
    critical_fields.append(csrf_field)
    for field in critical_fields:
        critical_arguments[field] = user_arguments_dict.get(field, [''])

    (validate_status, accepted) = validate_input_and_cert(
        critical_arguments,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    hosturl = accepted['HOSTURL'][-1]
    hostidentifier = accepted['HOSTIDENTIFIER'][-1]
    if hostidentifier:
        action = 'update'
    else:
        action = 'create'
        hostidentifier = keyword_auto
        accepted['HOSTIDENTIFIER'] = [hostidentifier]
    resource_id = "%s.%s" % (hosturl, hostidentifier)

    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)

    # Override original critical values with the validated ones

    for field in critical_fields:
        user_arguments_dict[field] = accepted[field]

    status = returnvalues.OK

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Resource edit actions'
    output_objects.append({
        'object_type': 'header',
        'text': 'Resource edit actions'
    })
    conf = prepare_conf(configuration, user_arguments_dict, resource_id)
    if 'create' == action:
        logger.info('%s is trying to create resource %s (%s)' %
                    (client_id, hosturl, conf))
        output_objects.append({
            'object_type': 'sectionheader',
            'text': 'Creating resource configuration'
        })

        # We only get here if hostidentifier is dynamic so no access control

        if not handle_update(configuration, client_id, resource_id, conf,
                             output_objects, True):
            status = returnvalues.SYSTEM_ERROR
    elif 'update' == action:
        logger.info('%s is trying to update resource %s (%s)' %
                    (client_id, resource_id, conf))
        output_objects.append({
            'object_type':
            'sectionheader',
            'text':
            'Updating existing resource configuration'
        })

        # Prevent unauthorized access to existing resources

        (owner_status, owner_list) = resource_owners(configuration,
                                                     resource_id)
        if not owner_status:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "Could not look up '%s' owners - no such resource?" %
                resource_id
            })
            status = returnvalues.SYSTEM_ERROR
        elif client_id in owner_list:
            if not handle_update(configuration, client_id, resource_id, conf,
                                 output_objects, False):
                status = returnvalues.SYSTEM_ERROR
        else:
            status = returnvalues.CLIENT_ERROR
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'You can only update your own resources!'
            })
    else:
        status = returnvalues.CLIENT_ERROR
        output_objects.append({
            'object_type': 'error_text',
            'text': 'Unknown action request!'
        })

    return (output_objects, status)
Ejemplo n.º 39
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 STATUS store ----------'
                          })

    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]
    store_name_list = accepted['store_name']
    all = accepted['all'][-1].lower() == 'true'
    parallel = accepted['parallel'][-1].lower() == 'true'

    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 get status for the store!'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    exit_status = returnvalues.OK

    if all:
        store_name_list = get_all_store_names(unique_resource_name)

    # take action based on supplied list of stores

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

    workers = []
    for store_name in store_name_list:
        task = Worker(target=status_resource_store,
                      args=(unique_resource_name, store_name,
                      configuration.resource_home, logger))
        workers.append((store_name, [task]))
        task.start()
        if not parallel:
            task.join()

    for (store_name, task_list) in workers:
        (status, msg) = task_list[0].finish()
        output_objects.append({'object_type': 'header', 'text'
                              : 'Status store'})
        if not status:
            output_objects.append({'object_type': 'error_text', 'text'
                                  : 'Problems getting store status: %s'
                                   % msg})
            exit_status = returnvalues.SYSTEM_ERROR
        else:
            output_objects.append({'object_type': 'text', 'text'
                                  : 'Status command run, output: %s'
                                   % msg})
    return (output_objects, exit_status)
Ejemplo n.º 40
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)

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

    # prepare support for confirm dialog and toggling the views (by css/jquery)

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = "Administrate %s: %s" % \
                          (configuration.site_vgrid_label, vgrid_name)

    title_entry['style'] = themed_styles(configuration)
    title_entry['javascript'] = '''
<script type="text/javascript" src="/images/js/jquery.js"></script>
<script type="text/javascript" src="/images/js/jquery-ui.js"></script>
<script type="text/javascript" src="/images/js/jquery.confirm.js"></script>

<script type="text/javascript" >

    var toggleHidden = function(classname) {
        // classname supposed to have a leading dot 
        $(classname).toggleClass('hidden');
    }

$(document).ready(function() {

          // init confirmation dialog
          $( "#confirm_dialog" ).dialog(
              // see http://jqueryui.com/docs/dialog/ for options
              { autoOpen: false,
                modal: true, closeOnEscape: true,
                width: 500,
                buttons: {
                   "Cancel": function() { $( "#" + name ).dialog("close"); }
                }
              });
     }
);
</script>
'''

    output_objects.append({'object_type': 'html_form',
                           'text':'''
 <div id="confirm_dialog" title="Confirm" style="background:#fff;">
  <div id="confirm_text"><!-- filled by js --></div>
   <textarea cols="40" rows="4" id="confirm_input" style="display:none;"></textarea>
 </div>
'''                       })
    
    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 })
        js_name = 'reqvgridowner%s' % hexlify(vgrid_name)
        helper = html_post_helper(js_name, 'sendrequestaction.py',
                                  {'vgrid_name': vgrid_name,
                                   'request_type': 'vgridowner',
                                   'request_text': ''})
        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',
             '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']):
        output_objects.append({'object_type': 'sectionheader',
                               'text': "%ss" % item.title()
                               })
        if item == 'trigger':
            # Always run as rule creator to avoid users being able to act on
            # behalf of ANY other user using triggers (=exploit)
            extra_fields = [('path', None),
                            ('changes', [keyword_all] + valid_trigger_changes),
                            ('run_as', client_id),
                            ('action', [keyword_auto] + valid_trigger_actions),
                            ('arguments', None)]
        else:
            extra_fields = []

        (status, oobjs) = vgrid_add_remove_table(client_id, vgrid_name, item, 
                                                 scr, configuration,
                                                 extra_fields)
        if not 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',
                 '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',
                 'title': 'Toggle view',
                 'text': 'Show %ss' % item.title() })
            output_objects.append({'object_type': 'html_form', 
                                   'text': '</div>' })

    # Checking/fixing of missing components

    output_objects.append({'object_type': 'sectionheader',
                           'text': "Repair/Add Components"})
    output_objects.append({'object_type': 'html_form',
                           'text': '''
      <form method="post" action="updatevgrid.py">
          <input type="hidden" name="vgrid_name" value="%(vgrid)s" />
          <input type="submit" value="Repair components" />
      </form>
''' % {'vgrid': vgrid_name}})

    output_objects.append({'object_type': 'sectionheader',
                           'text': "Delete %s " % vgrid_name})
    output_objects.append({'object_type': 'html_form',
                           'text': '''
To delete <b>%(vgrid)s</b> remove all members and owners ending with yourself.
''' % {'vgrid': vgrid_name}})

    return (output_objects, returnvalues.OK)
Ejemplo n.º 41
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)
    
    if not correct_handler('POST'):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Only accepting POST requests to prevent unintended updates'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    re_name = accepted['re_name'][-1]

    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 = get_re_dict(re_name, configuration)
    if not re_dict[0]:
        output_objects.append(
            {'object_type': 'error_text',
             'text': 'Could not read runtime environment details for %s'
             % re_name})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    # Make sure the runtime environment belongs to the user trying to delete it
    if client_id != re_dict[0]['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', 'title':
                               'Show runtime environments',
                               'text': 'Show runtime environments'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Delete the runtime environment
    (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 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',
                               'title': 'Show runtime environments',
                               'text': 'Show runtime environments'})
        return (output_objects, returnvalues.OK) 
Ejemplo n.º 42
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)

    if not correct_handler('POST'):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Only accepting POST requests to prevent unintended updates'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    resource_list = accepted['unique_resource_name']
    resource_id = resource_list.pop()

    res_dir = os.path.join(configuration.resource_home, resource_id)

    # Prevent unauthorized access
    
    (owner_status, owner_list) = resource_owners(configuration, resource_id)
    if not owner_status:
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : "Could not look up '%s' owners - no such resource?" % resource_id
             })
        return (output_objects, returnvalues.CLIENT_ERROR)
    elif client_id not in owner_list:
        logger.warning('user %s tried to delete resource "%s" not owned' % \
                       (client_id, resource_id))
        output_objects.append({'object_type': 'error_text', 'text'
                               : "You can't delete '%s' - you don't own it!"
                               % resource_id})
        output_objects.append({'object_type': 'link', 'destination':
                               'resman.py', 'class': 'infolink', 'title':
                               'Show resources', 'text': 'Show resources'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Locking the access to resources and vgrids.
    lock_path_vgrid = os.path.join(configuration.resource_home, "vgrid.lock")
    lock_handle_vgrid = open(lock_path_vgrid, 'a')

    fcntl.flock(lock_handle_vgrid.fileno(), fcntl.LOCK_EX)

    lock_path_res = os.path.join(configuration.resource_home, "resource.lock")
    lock_handle_res = open(lock_path_res, 'a')

    fcntl.flock(lock_handle_res.fileno(), fcntl.LOCK_EX)

    # Only resources that are down may be deleted.
    # A "FE.PGID" file with a PGID in the resource's home directory means that
    # the FE is running.

    pgid_path = os.path.join(res_dir, 'FE.PGID')
    fe_running = True
    try:

        # determine if fe runs by finding out if pgid is numerical

        pgid_file = open(pgid_path, 'r')
        fcntl.flock(pgid_file, fcntl.LOCK_EX)
        pgid = pgid_file.readline().strip()
        fcntl.flock(pgid_file, fcntl.LOCK_UN)
        pgid_file.close()
        if not pgid.isdigit():
            raise Exception('FE already stopped')
    except:
        fe_running = False

    if fe_running:
        output_objects.append({'object_type': 'error_text', 'text'
                               : "Can't delete the running resource %s!"
                               % resource_id})
        output_objects.append({'object_type': 'link', 'destination':
                               'resman.py', 'class': 'infolink', 'title':
                               'Show resources', 'text': 'Show resources'})
        lock_handle_vgrid.close()
        lock_handle_res.close()
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Deleting the resource files, but not the resource directory itself.
    # The resource directory is kept, to prevent hijacking of resource id's

    try:
        for name in os.listdir(res_dir):
            file_path = os.path.join(res_dir, name)
            if os.path.isfile(file_path):
                os.unlink(file_path)
    except Exception, err:
        output_objects.append({'object_type': 'error_text', 'text'
                               : 'Deletion exception: ' + str(err)})
        output_objects.append({'object_type': 'link', 'destination':
                               'resman.py', 'class': 'infolink', 'title':
                               'Show resources', 'text': 'Show resources'})
        lock_handle_vgrid.close()
        lock_handle_res.close()
        return (output_objects, returnvalues.CLIENT_ERROR)
Ejemplo n.º 43
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 anon_map.has_key(user_id):
            user_id = anon_map[user_id]
        if not user_map.has_key(user_id):
            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 not res_map.has_key(unique_resource_name):
            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 anon_map.has_key(unique_resource_name):
            unique_resource_name = anon_map[unique_resource_name]
        target_name = unique_resource_name
        res_map = get_resource_map(configuration)
        if not res_map.has_key(unique_resource_name):
            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))
    })
    output_objects.append({
        'object_type':
        'text',
        'text':
        """Please make sure you have notifications
configured on your Setings page if you expect a reply to this message"""
    })

    return (output_objects, returnvalues.OK)
Ejemplo n.º 44
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)
    logger.debug('Extracting input in %s' % op_name)
    client_dir = client_id_dir(client_id)
    status = returnvalues.OK
    defaults = signature()[1]
    
    logger.info('Extracted input in %s: %s' % (op_name,
                                               user_arguments_dict.keys()))

    # All non-file fields must be validated
    validate_args = dict([(key, user_arguments_dict.get(key, val)) for \
                         (key, val) in user_arguments_dict.items() if not key \
                          in manual_validation])
    (validate_status, accepted) = validate_input_and_cert(
        validate_args,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
        )
    if not validate_status:
        logger.error('%s validation failed: %s (%s)' % \
                     (op_name, validate_status, accepted))
        return (accepted, returnvalues.CLIENT_ERROR)

    logger.info('validated input in %s: %s' % (op_name, validate_args.keys()))

    if not correct_handler('POST'):
        logger.error('invalid method %s: %s' % (op_name, os.environ))
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Only accepting POST requests to prevent unintended updates'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    action = accepted['action'][-1]
    current_dir = os.path.normpath(accepted['current_dir'][-1])
    output_format = accepted['output_format'][-1]

    uploaded = []
    # Always include a files reply even if empty
    output_objects.append({'object_type': 'uploadfiles', 'files': uploaded})

    logger.info('parsing upload form in %s' % op_name)

    # 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
    cache_dir = os.path.join(base_dir, upload_tmp_dir) + os.sep

    # Now parse and validate files to archive
    # ... this includes checking for illegal directory traversal attempts

    for name in defaults.keys():
        if user_arguments_dict.has_key(name):
            del user_arguments_dict[name]

    try:
        (upload_files, upload_rejected) = parse_form_upload(
            user_arguments_dict, client_id, configuration, base_dir)
    except Exception, exc:
        logger.error('error extracting required fields: %s' % exc)
        return (output_objects, returnvalues.CLIENT_ERROR)
Ejemplo n.º 45
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')
    title_entry['text'] = 'Frozen Archives'
    (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)

    operation = accepted['operation'][-1]

    if not configuration.site_enable_freeze:
        output_objects.append({
            'object_type':
            'text',
            'text':
            '''Freezing archives is disabled on this site.
Please contact the site admins %s if you think it should be enabled.
''' % configuration.admin_email
        })
        return (output_objects, returnvalues.OK)

    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))
    if operation in show_operations:

        # jquery support for tablesorter and confirmation on delete
        # table initially sorted by col. 5 (State), 3 (Created date), 2 (name)

        if client_id in configuration.site_freeze_admins:
            permanent_flavors = []
        else:
            permanent_flavors = configuration.site_permanent_freeze
        # NOTE: must insert permanent_flavors list as string here
        refresh_call = 'ajax_freezedb(%s, "%s")' % (str(permanent_flavors),
                                                    keyword_final)
        table_spec = {
            'table_id': 'frozenarchivetable',
            'sort_order': '[[5,1],[3,1],[2,0]]',
            'refresh_call': refresh_call
        }
        (add_import, add_init,
         add_ready) = man_base_js(configuration, [table_spec])
        if operation == "show":
            add_ready += '%s;' % refresh_call
        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': 'Frozen Archives'
        })

        output_objects.append({
            'object_type':
            'text',
            'text':
            '''Frozen archives are write-once collections of files used e.g.
in relation to conference paper submissions. Please note that local policies
may prevent users from deleting frozen archives without explicit acceptance
from the management.
        '''
        })

        output_objects.append({
            'object_type': 'sectionheader',
            'text': 'Existing frozen archives'
        })

        # Helper form for removes

        form_method = 'post'
        csrf_limit = get_csrf_limit(configuration)
        target_op = 'deletefreeze'
        csrf_token = make_csrf_token(configuration, form_method, target_op,
                                     client_id, csrf_limit)
        helper = html_post_helper(
            'delfreeze', '%s.py' % target_op, {
                'freeze_id': '__DYNAMIC__',
                'flavor': '__DYNAMIC__',
                'target': TARGET_ARCHIVE,
                csrf_field: csrf_token
            })
        output_objects.append({'object_type': 'html_form', 'text': helper})

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

    frozenarchives = []
    if operation in list_operations:
        # NOTE: we do NOT enforce creator match here as edituser can't update
        #       without breaking any published archives
        (list_status, ret) = list_frozen_archives(configuration,
                                                  client_id,
                                                  strict_owner=False)
        if not list_status:
            logger.error("%s: failed for '%s': %s" % (op_name, client_id, ret))
            output_objects.append({'object_type': 'error_text', 'text': ret})
            return (output_objects, returnvalues.SYSTEM_ERROR)

        logger.debug("%s %s: building list of archives" % (op_name, operation))
        for freeze_id in ret:
            # TODO: add file count to meta and switch here
            # (load_status, freeze_dict) = get_frozen_meta(client_id, freeze_id,
            #                                             configuration)
            (load_status, freeze_dict) = get_frozen_archive(client_id,
                                                            freeze_id,
                                                            configuration,
                                                            checksum_list=[])
            if not load_status:
                logger.error("%s: load failed for '%s': %s" %
                             (op_name, freeze_id, freeze_dict))
                output_objects.append({
                    'object_type':
                    'error_text',
                    'text':
                    'Could not read details for "%s"' % freeze_id
                })
                return (output_objects, returnvalues.SYSTEM_ERROR)
            freeze_item = build_freezeitem_object(configuration,
                                                  freeze_dict,
                                                  summary=True)
            freeze_id = freeze_item['id']
            flavor = freeze_item.get('flavor', 'freeze')

            # Users may view all their archives
            freeze_item['viewfreezelink'] = {
                'object_type':
                'link',
                'destination':
                "showfreeze.py?freeze_id=%s;flavor=%s" % (freeze_id, flavor),
                'class':
                'infolink iconspace',
                'title':
                'View frozen archive %s' % freeze_id,
                'text':
                ''
            }
            # Users may edit pending archives
            if freeze_item['state'] != keyword_final:
                freeze_item['editfreezelink'] = {
                    'object_type': 'link',
                    'destination': "adminfreeze.py?freeze_id=%s" % freeze_id,
                    'class': 'adminlink iconspace',
                    'title': 'Edit archive %s' % freeze_id,
                    'text': ''
                }
            # Users may delete pending or non permanent archives.
            # Freeze admins may delete all their own archives.
            if freeze_item['state'] != keyword_final or \
                    flavor not in configuration.site_permanent_freeze or \
                    client_id in configuration.site_freeze_admins:
                freeze_item['delfreezelink'] = {
                    'object_type':
                    'link',
                    'destination':
                    "javascript: confirmDialog(%s, '%s', %s, %s);" %
                    ('delfreeze', 'Really remove %s?' % freeze_id, 'undefined',
                     "{freeze_id: '%s', flavor: '%s'}" % (freeze_id, flavor)),
                    'class':
                    'removelink iconspace',
                    'title':
                    'Remove %s' % freeze_id,
                    'text':
                    ''
                }

            frozenarchives.append(freeze_item)
        logger.debug("%s %s: inserting list of %d archives" %
                     (op_name, operation, len(frozenarchives)))

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

    if operation in show_operations:
        output_objects.append({
            'object_type': 'sectionheader',
            'text': 'Additional Frozen Archives'
        })
        output_objects.append({
            'object_type':
            'text',
            'text':
            """
You can create frozen snapshots/archives of particular subsets of your data in
order to make sure a verbatim copy is preserved. The freeze archive method
includes support for persistent publishing, so that you can e.g. reference your
data in publications. Backup archives can be used as a basic backup mechanism,
so that you can manually recover from any erroneous file removals."""
        })

        output_objects.append({
            'object_type':
            'html_form',
            'text':
            """<p>
Choose one of the archive methods below to make a manual archive:
</p>
<p>"""
        })
        output_objects.append({
            'object_type': 'link',
            'destination': 'adminfreeze.py?flavor=freeze',
            'class': 'addlink iconspace',
            'title': 'Make a new freeze archive of e.g. '
            'research data to be published',
            'text': 'Create a new freeze archive'
        })
        output_objects.append({'object_type': 'html_form', 'text': '</p><p>'})
        output_objects.append({
            'object_type':
            'link',
            'destination':
            'adminfreeze.py?flavor=backup',
            'class':
            'addlink iconspace',
            'title':
            'Make a new backup archive of %s data' % configuration.short_title,
            'text':
            'Create a new backup archive'
        })
        output_objects.append({
            'object_type': 'html_form',
            'text': "<br/><br/></p>"
        })

        if configuration.site_enable_duplicati:
            output_objects.append({
                'object_type':
                'text',
                'text':
                '''
Alternatively you can use Duplicati for traditional incremental backup/restore
with optional encryption of all your backup contents.'''
            })
            output_objects.append({
                'object_type':
                'html_form',
                'text':
                """
<p>For further details please refer to the """
            })
            output_objects.append({
                'object_type': 'link',
                'destination': 'setup.py?topic=duplicati',
                'class': '',
                'title': 'Open Duplicati settings',
                'text': 'Duplicati Settings'
            })
            output_objects.append({
                'object_type':
                'html_form',
                'text':
                """ and the %s documentation.<br/><br/></p>""" %
                configuration.short_title
            })

        if configuration.site_enable_seafile:
            output_objects.append({
                'object_type':
                'text',
                'text':
                '''
We recommend our Seafile sync solution for any small or medium sized data sets,
for which you want automatic file versioning and easy roll-back support.'''
            })
            output_objects.append({
                'object_type':
                'html_form',
                'text':
                """
<p>For further details please refer to the """
            })
            output_objects.append({
                'object_type': 'link',
                'destination': 'setup.py?topic=seafile',
                'class': '',
                'title': 'Open Seafile settings',
                'text': 'Seafile Settings'
            })
            output_objects.append({
                'object_type':
                'html_form',
                'text':
                """ and the %s documentation.</p>""" %
                configuration.short_title
            })
    logger.info("%s %s end for %s" % (op_name, operation, client_id))
    return (output_objects, returnvalues.OK)
Ejemplo n.º 46
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)

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Dashboard'

    # jquery support for tablesorter and confirmation on "leave":

    add_import, add_init, add_ready = '', '', ''
    add_init += '''
              function roundNumber(num, dec) {
              var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
              return result;
          }
    '''
    add_ready += '''
          $("#jobs_stats").addClass("spinner iconleftpad");
          $("#jobs_stats").html("Loading job stats...");
          $("#res_stats").addClass("spinner iconleftpad");
          $("#res_stats").html("Loading resource stats...");
          $("#disk_stats").addClass("spinner iconleftpad");
          $("#disk_stats").html("Loading disk stats...");
          $("#cert_stats").addClass("spinner iconleftpad");
          $("#cert_stats").html("Loading certificate information...");
          /* Run certificate request in the background and handle as soon as results come in */
          $.ajax({
              url: "userstats.py?output_format=json;stats=certificate",
              type: "GET",
              dataType: "json",
              cache: false,
              success: function(jsonRes, textStatus) {
                  var i = 0;
                  var certificate = null;
                  var renew_days = 30;
                  var day_msecs = 24*60*60*1000;
                  // Grab results from json response and place them in resource status.
                  for(i=0; i<jsonRes.length; i++) {
                      if (jsonRes[i].object_type == "user_stats") {    
                          certificate = jsonRes[i].certificate;
                          $("#cert_stats").removeClass("spinner iconleftpad");
                          $("#cert_stats").empty();
                          if (certificate.expire == -1) {
                              break;
                          }
                          var expire_date = new Date(certificate.expire);
                          $("#cert_stats").append("Your user certificate expires on " +
                          expire_date + ".");
                          // Use date from time diff in ms to avoid calendar mangling
                          var show_renew = new Date(expire_date.getTime() - renew_days*day_msecs);
                          if(new Date().getTime() > show_renew.getTime()) {
                              $("#cert_stats").addClass("warningtext");
                              $("#cert_stats").append("&nbsp;<a class=\'certrenewlink  iconspace\' href=\'reqcert.py\'>Renew certificate</a>.");
                          }
                          break;
                      }
                  }
              }
          });
          /* Run jobs request in the background and handle as soon as results come in */
          $.ajax({
              url: "userstats.py?output_format=json;stats=jobs",
              type: "GET",
              dataType: "json",
              cache: false,
              success: function(jsonRes, textStatus) {
                  var i = 0;
                  var jobs = null;
                  // Grab results from json response and place them in job status.
                  for(i=0; i<jsonRes.length; i++) {
                      if (jsonRes[i].object_type == "user_stats") {    
                          jobs = jsonRes[i].jobs;
                          //alert("inspect stats result: " + jobs);
                          $("#jobs_stats").removeClass("spinner iconleftpad");
                          $("#jobs_stats").empty();
                          $("#jobs_stats").append("You have submitted a total of " + jobs.total +
                              " jobs: " + jobs.parse + " parse, " + jobs.queued + " queued, " +
                              jobs.frozen + " frozen, " + jobs.executing + " executing, " +
                              jobs.finished + " finished, " + jobs.retry + " retry, " +
                              jobs.canceled + " canceled, " + jobs.expired + " expired and " +
                              jobs.failed + " failed.");
                         break;
                      }
                  }   
              }
          });
          /* Run resources request in the background and handle as soon as results come in */
          $.ajax({
              url: "userstats.py?output_format=json;stats=resources",
              type: "GET",
              dataType: "json",
              cache: false,
              success: function(jsonRes, textStatus) {
                  var i = 0;
                  var resources = null;
                  // Grab results from json response and place them in resource status.
                  for(i=0; i<jsonRes.length; i++) {
                      if (jsonRes[i].object_type == "user_stats") {    
                          resources = jsonRes[i].resources;
                          //alert("inspect resources stats result: " + resources);
                          $("#res_stats").removeClass("spinner iconleftpad");
                          $("#res_stats").empty();
                          $("#res_stats").append(resources.resources + " resources providing " +
                          resources.exes + " execution units in total allow execution of your jobs.");
                          break;
                      }
                  }
              }
          });
          /* Run disk request in the background and handle as soon as results come in */
          $.ajax({
              url:"userstats.py?output_format=json;stats=disk",
              type: "GET",
              dataType: "json",
              cache: false,
              success: function(jsonRes, textStatus) {
                  var i = 0;
                  var disk = null;
                  // Grab results from json response and place them in resource status.
                  for(i=0; i<jsonRes.length; i++) {
                      if (jsonRes[i].object_type == "user_stats") {    
                          disk = jsonRes[i].disk;
                          //alert("inspect disk stats result: " + disk);
                          $("#disk_stats").removeClass("spinner iconleftpad");
                          $("#disk_stats").empty();
                          $("#disk_stats").append("Your own " + disk.own_files +" files and " +
                              disk.own_directories + " directories take up " + roundNumber(disk.own_megabytes, 2) +
                              " MB in total and you additionally share " + disk.vgrid_files +
                              " files and " + disk.vgrid_directories + " directories of " +
                              roundNumber(disk.vgrid_megabytes, 2) + " MB in total.");
                          break;
                      }
                  }
              }
          });
    '''
    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': 'Dashboard'})
    output_objects.append({'object_type': 'sectionheader', 'text':
                           "Welcome to the %s" %
                           configuration.site_title})
    welcome_line = "Hi %s" % extract_field(client_id, "full_name")
    output_objects.append({'object_type': 'text', 'text': welcome_line})
    dashboard_info = """
This is your private entry page or your dashboard where you can get a
quick status overview and find pointers to help and documentation.
When you are logged in with your user credentials/certificate, as you are now,
you can navigate your pages using the menu on the left.
""" % os.environ
    output_objects.append({'object_type': 'text', 'text': dashboard_info})

    output_objects.append({'object_type': 'sectionheader', 'text':
                           "Your Status"})
    output_objects.append({'object_type': 'html_form', 'text': '''
<p>
This is a general status overview for your Grid activities. Please note that some
of the numbers are cached for a while to keep server load down.
</p>
<div id="jobs_stats"><!-- for jquery --></div><br />
<div id="res_stats"><!-- for jquery --></div><br />
<div id="disk_stats"><!-- for jquery --></div><br />
<div id="cert_stats"><!-- for jquery --></div><br />
<div id="cert_renew" class="hidden"><a href="reqcert.py">renew certificate</a>
</div>
'''})

    output_objects.append({'object_type': 'sectionheader', 'text':
                           'Documentation and Help'})
    online_help = """
%s includes some built-in documentation like the
""" % configuration.site_title
    output_objects.append({'object_type': 'text', 'text': online_help})
    output_objects.append({'object_type': 'link', 'destination': 'docs.py',
                           'class': 'infolink iconspace', 'title': 'built-in documentation',
                           'text': 'Docs page'})
    project_info = """
but additional help, background information and tutorials are available in the
"""
    output_objects.append({'object_type': 'text', 'text': project_info})
    output_objects.append({'object_type': 'link', 'destination':
                           configuration.site_external_doc,
                           'class': 'urllink iconspace', 'title':
                           'external documentation',
                           'text': 'external %s documentation' %
                           configuration.site_title})

    output_objects.append({'object_type': 'sectionheader', 'text':
                           "Personal Settings"})
    settings_info = """
You can customize your personal pages by opening the Settings
page from the navigation menu and entering personal preferences. In that way you
can completely redecorate your interface and configure things like notification,
profile visibility and remote file access.
"""
    output_objects.append({'object_type': 'text', 'text': settings_info})

    #env_info = """Env %s""" % os.environ
    #output_objects.append({'object_type': 'text', 'text': env_info})

    return (output_objects, returnvalues.OK)
Ejemplo n.º 47
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)

    valid_langs = {'sh': 'shell', 'python': 'python'}
    valid_flavors = {'user': '******',
                     'resource': 'vgridscriptgen'}
    defaults = signature()[1]
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
        )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    if not correct_handler('POST'):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Only accepting POST requests to prevent unintended updates'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    flags = ''.join(accepted['flags'])
    langs = accepted['lang']
    flavor_list = accepted['flavor']
    sh_cmd = accepted['sh_cmd'][-1]
    python_cmd = accepted['python_cmd'][-1]

    flavors = []

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Script generator'
    output_objects.append({'object_type': 'header', 'text'
                          : 'Script generator'})

    status = returnvalues.OK

    # 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 'h' in flags:
        output_objects = usage(output_objects, valid_langs,
                               valid_flavors)
        return (output_objects, status)

    # Filter out any invalid flavors to avoid illegal filenames, etc.

    for f in flavor_list:
        if f in valid_flavors.keys():
            flavors.append(f)

    # Default to user scripts

    if not flavors:
        if flavor_list:
            output_objects.append({'object_type': 'text', 'text'
                                  : 'No valid flavors specified - falling back to user scripts'
                                  })
        flavors = ['user']

    # Generate scripts in a "unique" destination directory
    # gmtime([seconds]) -> (tm_year, tm_mon, tm_day, tm_hour, tm_min,
    #                       tm_sec, tm_wday, tm_yday, tm_isdst)

    now = time.gmtime()
    timestamp = '%.2d%.2d%.2d-%.2d%.2d%.2d' % (
        now[2],
        now[1],
        now[0],
        now[3],
        now[4],
        now[5],
        )

    if not langs:

        # Add new languages here

        languages = [(usergen.sh_lang, sh_cmd, usergen.sh_ext),
                     (usergen.python_lang, python_cmd,
                     usergen.python_ext)]
    else:
        languages = []

        # check arguments

        for lang in langs:
            if lang == 'sh':
                interpreter = sh_cmd
                extension = usergen.sh_ext
            elif lang == 'python':
                interpreter = python_cmd
                extension = usergen.python_ext
            else:
                output_objects.append({'object_type': 'warning', 'text'
                        : 'Unknown script language: %s - ignoring!'
                         % lang})
                continue

            languages.append((lang, interpreter, extension))

    if not languages:
        output_objects.append({'object_type': 'error_text', 'text'
                              : 'No valid languages specified - aborting script generation'
                              })
        return (output_objects, returnvalues.CLIENT_ERROR)

    for flavor in flavors:
        script_dir = '%s-%s-scripts-%s' % (configuration.short_title, flavor, timestamp)
        dest_dir = '%s%s' % (base_dir, script_dir)

        if not os.path.isdir(dest_dir):
            try:
                os.mkdir(dest_dir)
            except Exception, exc:
                output_objects.append({'object_type': 'error_text',
                        'text'
                        : 'Failed to create destination directory (%s) - aborting script generation'
                         % exc})
                return (output_objects, returnvalues.SYSTEM_ERROR)

        for (lang, _, _) in languages:
            output_objects.append({'object_type': 'text', 'text'
                                  : 'Generating %s %s scripts in the %s subdirectory of your %s home directory'
                                   % (lang, flavor, script_dir, configuration.short_title )})

        # Generate all scripts

        if flavor == 'user':
            for op in usergen.script_ops:
                generator = 'usergen.generate_%s' % op
                eval(generator)(languages, dest_dir)

            if usergen.shared_lib:
                usergen.generate_lib(usergen.script_ops, languages,
                        dest_dir)

            if usergen.test_script:
                usergen.generate_test(languages, dest_dir)
        elif flavor == 'resource':
            for op in vgridgen.script_ops_single_arg:
                vgridgen.generate_single_argument(op[0], op[1],
                        languages, dest_dir)
            for op in vgridgen.script_ops_single_upload_arg:
                vgridgen.generate_single_argument_upload(op[0], op[1],
                        op[2], languages, dest_dir)
            for op in vgridgen.script_ops_two_args:
                vgridgen.generate_two_arguments(op[0], op[1], op[2],
                        languages, dest_dir)
            for op in vgridgen.script_ops_ten_args:
                vgridgen.generate_ten_arguments(op[0], op[1], op[2], op[3],
                                                  op[4], op[5], op[6], op[7],
                                                  op[8], op[9], op[10],
                                                  languages, dest_dir)
        else:
            output_objects.append({'object_type': 'warning_text', 'text'
                                  : 'Unknown flavor: %s' % flavor})
            continue

        # Always include license conditions file
        
        usergen.write_license(dest_dir)
        
        output_objects.append({'object_type': 'text', 'text': '... Done'
                              })
        output_objects.append({'object_type': 'text', 'text'
                              : '%s %s scripts are now available in your %s home directory:'
                               % (configuration.short_title, flavor, configuration.short_title)})
        output_objects.append({'object_type': 'link', 'text'
                              : 'View directory', 'destination'
                              : 'fileman.py?path=%s/' % script_dir})

        # Create zip from generated dir

        output_objects.append({'object_type': 'text', 'text'
                              : 'Generating zip archive of the %s %s scripts'
                               % (configuration.short_title, flavor)})

        script_zip = script_dir + '.zip'
        dest_zip = '%s%s' % (base_dir, script_zip)
        # Force compression
        zip_file = zipfile.ZipFile(dest_zip, 'w', zipfile.ZIP_DEFLATED)

        # Directory write is not supported - add each file manually

        for script in os.listdir(dest_dir):
            zip_file.write(dest_dir + os.sep + script, script_dir
                            + os.sep + script)

        # Preserve executable flag in accordance with:
        # http://mail.python.org/pipermail/pythonmac-sig/2005-March/013491.html

        for zinfo in zip_file.filelist:
            zinfo.create_system = 3

        zip_file.close()

        # Verify CRC

        zip_file = zipfile.ZipFile(dest_zip, 'r')
        err = zip_file.testzip()
        zip_file.close()
        if err:
            output_objects.append({'object_type': 'error_text', 'text'
                                  : 'Zip file integrity check failed! (%s)'
                                   % err})
            status = returnvalues.SYSTEM_ERROR
            continue

        output_objects.append({'object_type': 'text', 'text': '... Done'
                              })
        output_objects.append({'object_type': 'text', 'text'
                              : 'Zip archive of the %s %s scripts are now available in your %s home directory'
                               % (configuration.short_title, flavor, configuration.short_title)})
        output_objects.append({'object_type': 'link', 'text'
                              : 'Download zip archive', 'destination'
                              : os.path.join('..', client_dir,
                              script_zip)})
Ejemplo n.º 48
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 store ----------'
    })

    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]
    store_name_list = accepted['store_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 store!'
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    exit_status = returnvalues.OK

    if all:
        store_name_list = get_all_store_names(unique_resource_name)

    # take action based on supplied list of stores

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

    workers = []
    for store_name in store_name_list:
        task = Worker(target=stop_resource_store,
                      args=(unique_resource_name, store_name,
                            configuration.resource_home, logger))
        workers.append((store_name, [task]))
        task.start()
        if not parallel:
            task.join()

    # Complete each stop thread before launching corresponding start threads

    for (store_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_store,
                      args=(unique_resource_name, store_name,
                            configuration.resource_home, logger))
        task_list.append(task)
        task.start()
        if not parallel:
            task.join()

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

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

    return (output_objects, exit_status)
Ejemplo n.º 49
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)

    if not correct_handler('POST'):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Only accepting POST requests to prevent unintended updates'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    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 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)
Ejemplo n.º 50
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,
        )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    if not correct_handler('POST'):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Only accepting POST requests to prevent unintended updates'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    flags = ''.join(accepted['flags'])
    patterns = accepted['path']
    current_dir = accepted['current_dir']

    # 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
        # NB: Globbing disabled on purpose here

        unfiltered_match = [base_dir + pattern]
        match = []
        for server_path in unfiltered_match:
            real_path = os.path.abspath(server_path)
            if not valid_user_path(real_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, real_path, pattern))
                continue
            match.append(real_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: cannot remove directory '%s': Permission denied"
                 % (op_name, pattern)})
            status = returnvalues.CLIENT_ERROR

        for real_path in match:
            relative_path = real_path.replace(base_dir, '')
            if verbose(flags):
                output_objects.append({'object_type': 'file', 'name'
                        : relative_path})
            if not os.path.exists(real_path):
                output_objects.append({'object_type': 'file_not_found',
                        'name': relative_path})
                continue
            try:
                if parents(flags):

                    # Please note that 'rmdir -p X' should give error if X
                    #  doesn't exist contrary to 'mkdir -p X' not giving error
                    # if X exists.

                    os.removedirs(real_path)
                else:
                    os.rmdir(real_path)
            except Exception, exc:
                output_objects.append({'object_type': 'error_text',
                        'text': "%s failed on '%s'" % (op_name,
                        relative_path)})
                logger.error("%s: failed on '%s': %s" % (op_name,
                             relative_path, exc))
                status = returnvalues.SYSTEM_ERROR
                continue
Ejemplo n.º 51
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'
                          : 'Add %s Owner' % configuration.site_vgrid_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)

    if not correct_handler('POST'):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Only accepting POST requests to prevent unintended updates'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    vgrid_name = accepted['vgrid_name'][-1].strip()
    cert_id = accepted['cert_id'][-1].strip()
    cert_dir = client_id_dir(cert_id)
    # inherited vgrid membership
    inherit_vgrid_member = False

    # Allow openid alias as subject if openid with alias is enabled
    if configuration.user_openid_providers and configuration.user_openid_alias:
        cert_id = expand_openid_alias(cert_id, configuration)

    # 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, _) = \
        init_vgrid_script_add_rem(vgrid_name, client_id, cert_id,
                                  'owner', configuration)
    if not ret_val:
        output_objects.append({'object_type': 'error_text', 'text'
                              : msg})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # don't add if already an owner

    if vgrid_is_owner(vgrid_name, cert_id, configuration):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : '%s is already an owner of %s or a parent %s.'
             % (cert_id, vgrid_name, configuration.site_vgrid_label)})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # don't add if already a direct member

    if vgrid_is_member(vgrid_name, cert_id, configuration, recursive=False):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : '%s is already a member of %s - please remove first.'
             % (cert_id, vgrid_name)})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # owner of subvgrid?

    (status, subvgrids) = vgrid_list_subvgrids(vgrid_name,
            configuration)
    if not status:
        output_objects.append({'object_type': 'error_text', 'text'
                              : 'Error getting list of sub%ss: %s'
                               % (configuration.site_vgrid_label, subvgrids)})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    for subvgrid in subvgrids:
        if vgrid_is_owner(subvgrid, cert_id, configuration, recursive=False):
            output_objects.append(
                {'object_type': 'error_text', 'text'
                 : """%s is already an owner of a sub-%s ('%s'). Please
remove the person first and then try this operation again.""" % \
                 (cert_id, configuration.site_vgrid_label, subvgrid)})
            return (output_objects, returnvalues.CLIENT_ERROR)
        if vgrid_is_member(subvgrid, cert_id, configuration, recursive=False):
            output_objects.append(
                {'object_type': 'error_text', 'text'
                 : """%s is already a member of a sub-%s ('%s'). Please
remove the person first and then try this operation again.""" % \
                 (cert_id, configuration.site_vgrid_label, subvgrid)})
            return (output_objects, returnvalues.CLIENT_ERROR)

    # we DO allow ownership if member of parent vgrid - only handle with care

    if vgrid_is_member(vgrid_name, cert_id, configuration):
        # list is in top-down order 
        parent_vgrids = vgrid_list_parents(vgrid_name, configuration)
        inherit_vgrid_member = vgrid_name
        for parent in parent_vgrids:
            if vgrid_is_member(parent, cert_id, configuration,
                               recursive=False):
                inherit_vgrid_member = parent
                break
        output_objects.append(
            {'object_type': 'text', 'text'
             : '''NOTE: %s is already a member of parent %s %s.''' % \
             (cert_id, configuration.site_vgrid_label, inherit_vgrid_member)
             })

    # getting here means cert_id is not owner of any parent or child vgrids.
    # may still be member of a parent grid but not a child vgrid.

    public_base_dir = \
        os.path.abspath(os.path.join(configuration.vgrid_public_base,
                        vgrid_name)) + os.sep
    private_base_dir = \
        os.path.abspath(os.path.join(configuration.vgrid_private_base,
                        vgrid_name)) + os.sep

    # 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

    user_dir = os.path.abspath(os.path.join(configuration.user_home,
                               cert_dir)) + os.sep

    user_public_base = os.path.abspath(os.path.join(user_dir,
            'public_base')) + os.sep
    user_private_base = os.path.abspath(os.path.join(user_dir,
            'private_base')) + os.sep

    # make sure all dirs can be created (that a file or directory with the same
    # name do not exist prior to adding the owner)

    if os.path.exists(user_public_base + vgrid_name):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : '''Could not add owner, a file or directory in public_base
exists with the same name! %s''' % user_dir + vgrid_name})
        return (output_objects, returnvalues.CLIENT_ERROR)

    if os.path.exists(user_private_base + vgrid_name):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : '''Could not add owner, a file or directory in private_base
exists with the same name!'''})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # vgrid share already exists if user is a member of parent vgrid
    
    if not inherit_vgrid_member and os.path.exists(user_dir + vgrid_name):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : '''Could not add owner, a file or directory in the home
directory exists with the same name!'''})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Add

    (add_status, add_msg) = vgrid_add_owners(configuration, vgrid_name,
                                             [cert_id])
    if not add_status:
        output_objects.append({'object_type': 'error_text', 'text'
                              : add_msg})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    vgrid_name_parts = vgrid_name.split('/')
    is_subvgrid = len(vgrid_name_parts) > 1

    # create public_base in cert_ids home dir if it does not exists

    try:
        os.mkdir(user_public_base)
    except Exception, exc:
        pass
Ejemplo n.º 52
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 %s Member' % configuration.site_vgrid_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)

    if not correct_handler('POST'):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Only accepting POST requests to prevent unintended updates'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    vgrid_name = accepted['vgrid_name'][-1]
    cert_id = accepted['cert_id'][-1]
    cert_dir = client_id_dir(cert_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, _) = \
        init_vgrid_script_add_rem(vgrid_name, client_id, cert_id,
                                  'member', configuration)
    if not ret_val:
        output_objects.append({'object_type': 'error_text', 'text'
                              : msg})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # don't remove if not a member

    if not vgrid_is_member(vgrid_name, cert_id, configuration):
        output_objects.append({'object_type': 'error_text', 'text'
                              : '%s is not a member of %s or a parent %s.'
                               % (cert_id, vgrid_name,
                                  configuration.site_vgrid_label)})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # owner of subvgrid?

    (status, subvgrids) = vgrid_list_subvgrids(vgrid_name,
            configuration)
    if not status:
        output_objects.append({'object_type': 'error_text', 'text'
                              : 'Error getting list of sub%ss: %s'
                               % (configuration.site_vgrid_label, subvgrids)})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    # TODO: we DO allow ownership of sub vgrids with parent membership so we
    # should support the (cumbersome) relinking of vgrid shares here. Leave it
    # to user to do it manually for now with temporary removal of ownership

    for subvgrid in subvgrids:
        if vgrid_is_owner(subvgrid, cert_id, configuration, recursive=False):
            output_objects.append(
                {'object_type': 'error_text', 'text'
                 : """%(cert_id)s is already an owner of a sub-%(_label)s
('%(subvgrid)s'). While we DO support members being owners of sub-%(_label)ss,
we do not support removing parent %(_label)s members at the moment. Please
(temporarily) remove the person as owner of all sub %(_label)ss first and then
try this operation again.""" % {'cert_id': cert_id, 'subvgrid': subvgrid,
                                '_label': configuration.site_vgrid_label}})
            return (output_objects, returnvalues.CLIENT_ERROR)

    # Please note that base_dir must end in slash to avoid access to other
    # vgrid dirs when own name is a prefix of another name

    base_dir = os.path.abspath(os.path.join(configuration.vgrid_home,
                               vgrid_name)) + os.sep

    # remove symlink from users home directory to vgrid directory

    # 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

    user_dir = os.path.abspath(os.path.join(configuration.user_home,
                               cert_dir)) + os.sep

    dst = user_dir + vgrid_name
    try:
        os.remove(dst)
    except Exception, exc:

        # ouch, not good. Email admin?

        pass
Ejemplo n.º 53
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 template.has_key('SOFTWARE'):
        new_sw = template['SOFTWARE'][:software_entries]
        template['SOFTWARE'] = new_sw
    if template.has_key('ENVIRONMENTVARIABLE'):
        new_env = template['ENVIRONMENTVARIABLE'][:environment_entries]
        template['ENVIRONMENTVARIABLE'] = new_env
    if template.has_key('TESTPROCEDURE'):
        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 template.has_key('SOFTWARE'):
            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 software.has_key('Sublevel') 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 template.has_key('TESTPROCEDURE'):
            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 template.has_key('VERIFYSTDOUT'):
                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 template.has_key('VERIFYSTDERR'):
                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 template.has_key('VERIFYSTATUS'):
                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 environmentvariable.has_key('Sublevel')\
         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 template.has_key('ENVIRONMENTVARIABLE'):
            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)
Ejemplo n.º 54
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)
Ejemplo n.º 55
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)

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'Advanced Shell'
    if accepted['menu'][-1] == 'no':
        title_entry['skipmenu'] = True
        title_entry['skipwidgets'] = True
        title_entry['style'] = '''
<style type="text/css">
#content { margin: 10px }
</style>'''
    # Please have href points to the CSS file and have basedir changed to the
    # directory where JavaScripts are placed.
    title_entry['script']['advanced'] += '''
<link rel="stylesheet" type="text/css" href="/images/css/shell.css" media="screen"/>
'''
    title_entry['script']['init'] = '''
  var  basedir="/images/js/";

  var shell;
  var interval;
  // scripts have to be loaded in sequence, thereby this recursion.
  var scripts=["toolkits.js","gui.js","ajax.js","intellisense.js",
               "output.js","status.js","lib.js","shell.js"];
  function loadAll(s_i) {
        if(s_i<1)
        {
            shell=new Shell('shell', 'shell', 'xmlrpcinterface.py');
            shell.Init();
            return;
        }
        var script=document.createElement('script');
        script.setAttribute('type','text/javascript');
        script.setAttribute('src',basedir + scripts[ scripts.length - s_i ]);
        document.getElementsByTagName('head')[0].appendChild(script);

        if ( script.readyState ) {
           // IE style browser
           script.onreadystatechange= function () {
                if(this.readyState=='loaded'||this.readyState=='complete')
                    loadAll( s_i-1 );
            }
        } else {
        // other browser, should support onload
            script.onload=function()
            {
                loadAll( s_i-1 );
            }
        }
        return;
    }
'''
    title_entry['script']['body'] = ' onload="loadAll(scripts.length);"'

    output_objects.append({'object_type': 'header', 'text': 'Advanced Shell'
                           })
    output_objects.append({'object_type': 'html_form',
                           'text': '<div id="shell"><!-- filled by js --></div>'})

    return (output_objects, status)
Ejemplo n.º 56
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'] = "Create Archive"
    # NOTE: Delay header entry here to include freeze flavor
    # All non-file fields must be validated
    validate_args = dict([(key, user_arguments_dict.get(key, val)) for
                          (key, val) in defaults.items()])
    # IMPORTANT: we must explicitly inlude CSRF token
    validate_args[csrf_field] = user_arguments_dict.get(csrf_field, [
                                                        'AllowMe'])
    (validate_status, accepted) = validate_input_and_cert(
        validate_args,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    flavor = accepted['flavor'][-1].strip()
    freeze_state = accepted['freeze_state'][-1].strip()

    if not safe_handler(configuration, 'post', op_name, client_id,
                        get_csrf_limit(configuration), accepted):
        output_objects.append(
            {'object_type': 'error_text', 'text': '''Only accepting
CSRF-filtered POST requests to prevent unintended updates'''
             })
        return (output_objects, returnvalues.CLIENT_ERROR)

    if not flavor in freeze_flavors.keys():
        output_objects.append({'object_type': 'error_text', 'text':
                               'Invalid freeze flavor: %s' % flavor})
        return (output_objects, returnvalues.CLIENT_ERROR)
    if not freeze_state in freeze_flavors[flavor]['states'] + [keyword_auto]:
        output_objects.append({'object_type': 'error_text', 'text':
                               'Invalid freeze state: %s' % freeze_state})
        return (output_objects, returnvalues.CLIENT_ERROR)

    title = freeze_flavors[flavor]['createfreeze_title']
    output_objects.append({'object_type': 'header', 'text': title})

    if not configuration.site_enable_freeze:
        output_objects.append({'object_type': 'text', 'text':
                               '''Freezing archives is disabled on this site.
Please contact the site admins %s if you think it should be enabled.
''' % configuration.admin_email})
        return (output_objects, returnvalues.OK)

    # jquery support for confirmation on freeze
    (add_import, add_init, add_ready) = man_base_js(configuration, [])
    title_entry['script']['advanced'] += add_import
    title_entry['script']['init'] += add_init
    title_entry['script']['ready'] += add_ready

    output_objects.append({'object_type': 'html_form',
                           'text': man_base_html(configuration)})

    freeze_id = accepted['freeze_id'][-1].strip()
    freeze_name = accepted['freeze_name'][-1].strip()
    freeze_description = accepted['freeze_description'][-1]
    freeze_author = accepted['freeze_author'][-1].strip()
    freeze_department = accepted['freeze_department'][-1].strip()
    freeze_organization = accepted['freeze_organization'][-1].strip()
    freeze_publish = accepted['freeze_publish'][-1].strip()
    do_publish = (freeze_publish.lower() in ('on', 'true', 'yes', '1'))

    # Share init of base meta with lookup of default state in freeze_flavors
    if not freeze_state or freeze_state == keyword_auto:
        freeze_state = freeze_flavors[flavor]['states'][0]
    freeze_meta = {'ID': freeze_id, 'STATE': freeze_state}

    # New archives must have name and description set
    if freeze_id == keyword_auto:
        logger.debug("creating a new %s archive for %s" % (flavor, client_id))
        if not freeze_name or freeze_name == keyword_auto:
            freeze_name = '%s-%s' % (flavor, datetime.datetime.now())
        if not freeze_description:
            if flavor == 'backup':
                freeze_description = 'manual backup archive created on %s' % \
                                     datetime.datetime.now()
            else:
                output_objects.append(
                    {'object_type': 'error_text', 'text':
                     'You must provide a description for the archive!'})
                return (output_objects, returnvalues.CLIENT_ERROR)
        if flavor == 'phd' and (not freeze_author or not freeze_department):
            output_objects.append({'object_type': 'error_text', 'text': """
You must provide author and department for the thesis!"""})
            return (output_objects, returnvalues.CLIENT_ERROR)
        freeze_meta.update(
            {'FLAVOR': flavor, 'NAME': freeze_name,
             'DESCRIPTION': freeze_description,
             'AUTHOR': freeze_author, 'DEPARTMENT': freeze_department,
             'ORGANIZATION': freeze_organization, 'PUBLISH': do_publish})
    elif is_frozen_archive(client_id, freeze_id, configuration):
        logger.debug("updating existing %s archive for %s" % (flavor,
                                                              client_id))
        # Update any explicitly provided fields (may be left empty on finalize)
        changes = {}
        if freeze_name and freeze_name != keyword_auto:
            changes['NAME'] = freeze_name
        if freeze_author:
            changes['AUTHOR'] = freeze_author
        if freeze_description:
            changes['DESCRIPTION'] = freeze_description
        if freeze_publish:
            changes['PUBLISH'] = do_publish
        logger.debug("updating existing %s archive for %s with: %s" %
                     (flavor, client_id, changes))
        logger.debug("publish is %s based on %s" %
                     (do_publish, freeze_publish))
        freeze_meta.update(changes)
    else:
        logger.error("no such %s archive for %s: %s" % (flavor, client_id,
                                                        freeze_id))
        output_objects.append({'object_type': 'error_text', 'text': """
Invalid archive ID %s - you must either create a new archive or edit an
existing archive of yours!""" % freeze_id})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Now parse and validate files to archive

    for name in defaults.keys():
        if user_arguments_dict.has_key(name):
            del user_arguments_dict[name]

    (copy_files, copy_rejected) = parse_form_copy(user_arguments_dict,
                                                  client_id, configuration)
    (move_files, move_rejected) = parse_form_move(user_arguments_dict,
                                                  client_id, configuration)
    (upload_files, upload_rejected) = parse_form_upload(user_arguments_dict,
                                                        client_id,
                                                        configuration)
    if copy_rejected + move_rejected + upload_rejected:
        output_objects.append({'object_type': 'error_text', 'text':
                               'Errors parsing freeze files: %s' %
                               '\n '.join(copy_rejected + move_rejected +
                                          upload_rejected)})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # NOTE: this may be a new or an existing pending archive, and it will fail
    #       if archive is already under update
    (retval, retmsg) = create_frozen_archive(freeze_meta, copy_files,
                                             move_files, upload_files,
                                             client_id, configuration)
    if not retval:
        output_objects.append({'object_type': 'error_text', 'text':
                               'Error creating/updating archive: %s'
                               % retmsg})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    # Make sure we have freeze_id and other updated fields
    freeze_meta.update(retmsg)
    freeze_id = freeze_meta['ID']
    logger.info("%s: successful for '%s': %s" % (op_name,
                                                 freeze_id, client_id))
    # Return simple status mainly for use in scripting
    output_objects.append({'object_type': 'freezestatus', 'freeze_id': freeze_id,
                           'flavor': flavor, 'freeze_state': freeze_state})
    publish_note = ''
    if freeze_state == keyword_pending:
        publish_hint = 'Preview published archive page in a new window/tab'
        publish_text = 'Preview publishing'
        output_objects.append({'object_type': 'text', 'text': """
Saved *preliminary* %s archive with ID %s . You can continue inspecting and
changing it until you're satisfied, then finalize it for actual persistent
freezing.""" % (flavor, freeze_id)})
    else:
        publish_hint = 'View published archive page in a new window/tab'
        publish_text = 'Open published archive'
        output_objects.append({'object_type': 'text', 'text':
                               'Successfully froze %s archive with ID %s .'
                               % (flavor, freeze_id)})

    if do_publish:
        public_url = published_url(freeze_meta, configuration)
        output_objects.append({'object_type': 'text', 'text': ''})
        output_objects.append({
            'object_type': 'link',
            'destination': public_url,
            'class': 'previewarchivelink iconspace genericbutton',
            'title': publish_hint,
            'text': publish_text,
            'target': '_blank',
        })
        output_objects.append({'object_type': 'text', 'text': ''})

    # Always allow show archive
    output_objects.append({
        'object_type': 'link',
        'destination': 'showfreeze.py?freeze_id=%s;flavor=%s' % (freeze_id,
                                                                 flavor),
        'class': 'viewarchivelink iconspace genericbutton',
        'title': 'View details about your %s archive' % flavor,
        'text': 'View details',
    })

    if freeze_state == keyword_pending:
        output_objects.append({'object_type': 'text', 'text': ''})
        output_objects.append({
            'object_type': 'link',
            'destination': 'adminfreeze.py?freeze_id=%s' % freeze_id,
            'class': 'editarchivelink iconspace genericbutton',
            'title': 'Further modify your pending %s archive' % flavor,
            'text': 'Edit archive',
        })
        output_objects.append({'object_type': 'text', 'text': ''})
        output_objects.append({'object_type': 'html_form', 'text': """
<br/><hr/><br/>
<p class='warn_message'>IMPORTANT: you still have to explicitly finalize your
archive before you get the additional data integrity/persistance guarantees
like tape archiving.
</p>"""})

        form_method = 'post'
        target_op = 'createfreeze'
        csrf_limit = get_csrf_limit(configuration)
        csrf_token = make_csrf_token(configuration, form_method, target_op,
                                     client_id, csrf_limit)
        helper = html_post_helper('createfreeze', '%s.py' % target_op,
                                  {'freeze_id': freeze_id,
                                   'freeze_state': keyword_final,
                                   'flavor': flavor,
                                   csrf_field: csrf_token})
        output_objects.append({'object_type': 'html_form', 'text': helper})

        output_objects.append({
            'object_type': 'link',
            'destination':
            "javascript: confirmDialog(%s, '%s');" %
            ('createfreeze', 'Really finalize %s?' % freeze_id),
            'class': 'finalizearchivelink iconspace genericbutton',
            'title': 'Finalize %s archive to prevent further changes' % flavor,
            'text': 'Finalize archive',
        })

    return (output_objects, returnvalues.OK)
Ejemplo n.º 57
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'
                          : 'Add %s Member' % configuration.site_vgrid_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)

    if not correct_handler('POST'):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Only accepting POST requests to prevent unintended updates'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    vgrid_name = accepted['vgrid_name'][-1].strip()
    cert_id = accepted['cert_id'][-1].strip()
    cert_dir = client_id_dir(cert_id)

    # Allow openid alias as subject if openid with alias is enabled
    if configuration.user_openid_providers and configuration.user_openid_alias:
        cert_id = expand_openid_alias(cert_id, configuration)

    # 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, _) = \
        init_vgrid_script_add_rem(vgrid_name, client_id, cert_id,
                                  'member', configuration)
    if not ret_val:
        output_objects.append({'object_type': 'error_text', 'text'
                              : msg})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # don't add if already an owner

    if vgrid_is_owner(vgrid_name, cert_id, configuration):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : '%s is already an owner of %s or a parent %s.'
             % (cert_id, vgrid_name, configuration.site_vgrid_label)})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # don't add if already a member

    if vgrid_is_member(vgrid_name, cert_id, configuration):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : '''%s is already a member of %s or a parent %s. Please remove
the person first and then try this operation again.''' % \
             (cert_id, vgrid_name, configuration.site_vgrid_label)
             })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # owner or member of subvgrid?

    (status, subvgrids) = vgrid_list_subvgrids(vgrid_name,
            configuration)
    if not status:
        output_objects.append({'object_type': 'error_text', 'text'
                              : 'Error getting list of sub%ss: %s'
                               % (configuration.site_vgrid_label, subvgrids)})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    # TODO: we DO allow ownership of sub vgrids with parent membership so we
    # should support the (cumbersome) relinking of vgrid shares here. Leave it
    # to user to do it manually for now with temporary removal of ownership

    for subvgrid in subvgrids:
        if vgrid_is_owner(subvgrid, cert_id, configuration, recursive=False):
            output_objects.append(
                {'object_type': 'error_text', 'text'
                 : """%(cert_id)s is already an owner of a sub-%(_label)s
('%(subvgrid)s'). While we DO support members being owners of sub-%(_label)ss,
we do not support adding parent %(_label)s members at the moment. Please
(temporarily) remove the person as owner of all sub-%(_label)ss first and then
try this operation again.""" % {'cert_id': cert_id, 'subvgrid': subvgrid,
                                '_label': configuration.site_vgrid_label}})
            return (output_objects, returnvalues.CLIENT_ERROR)
        if vgrid_is_member(subvgrid, cert_id, configuration, recursive=False):
            output_objects.append(
                {'object_type': 'error_text', 'text'
                 : """%s is already a member of a sub-%s ('%s'). Please
remove the person first and then try this operation again.""" % \
                 (cert_id, configuration.site_vgrid_label, subvgrid)})
            return (output_objects, returnvalues.CLIENT_ERROR)

    # getting here means cert_id is neither owner or member of any parent or
    # sub-vgrids.

    # Please note that base_dir must end in slash to avoid access to other
    # vgrid dirs when own name is a prefix of another name

    base_dir = os.path.abspath(os.path.join(configuration.vgrid_home,
                               vgrid_name)) + os.sep
    user_dir = os.path.abspath(os.path.join(configuration.user_home,
                               cert_dir)) + os.sep

    # make sure all dirs can be created (that a file or directory with the same
    # name do not exist prior to adding the member)

    if os.path.exists(user_dir + vgrid_name):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : '''Could not add member, a file or directory in the home
directory called %s exists! (%s)''' % (vgrid_name, user_dir + vgrid_name)})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Add

    (add_status, add_msg) = vgrid_add_members(configuration, vgrid_name,
                                              [cert_id])
    if not add_status:
        output_objects.append({'object_type': 'error_text', 'text'
                              : add_msg})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    vgrid_name_parts = vgrid_name.split('/')
    is_subvgrid = len(vgrid_name_parts) > 1

    if is_subvgrid:
        try:

            # vgrid_name = IMADA/STUD/BACH
            # vgrid_name_last_fragment = BACH

            vgrid_name_last_fragment = \
                vgrid_name_parts[len(vgrid_name_parts)
                                     - 1].strip()

            # vgrid_name_without_last_fragment = IMADA/STUD/

            vgrid_name_without_last_fragment = \
                ('/'.join(vgrid_name_parts[0:len(vgrid_name_parts)
                  - 1]) + os.sep).strip()

            # create dirs if they do not exist

            dir1 = user_dir + vgrid_name_without_last_fragment
            if not os.path.isdir(dir1):
                os.makedirs(dir1)
        except Exception, exc:

            # out of range? should not be possible due to is_subvgrid check

            output_objects.append(
                {'object_type': 'error_text', 'text'
                 : ('Could not create needed dirs on %s server! %s'
                    % (configuration.short_title, exc))})
            logger.error('%s when looking for dir %s.' % (exc, dir1))
            return (output_objects, returnvalues.SYSTEM_ERROR)
Ejemplo n.º 58
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 Forum' % label
    user_settings = title_entry.get('user_settings', {})
    (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]
    action = accepted['action'][-1]
    thread = accepted['thread'][-1]
    msg_subject = accepted['msg_subject'][-1].strip()
    msg_body = accepted['msg_body'][-1].strip()

    if not vgrid_is_owner_or_member(vgrid_name, client_id, configuration):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '''You must be an owner or member of %s %s to
access the forum.''' % (vgrid_name, label)
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    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)

    # 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.vgrid_private_base, vgrid_name)) + os.sep

    forum_base = os.path.abspath(os.path.join(base_dir, '.vgridforum'))

    # TODO: can we update style inline to avoid explicit themed_styles?
    title_entry['style'] = themed_styles(configuration,
                                         advanced=['forum.css'],
                                         user_settings=user_settings)
    add_import = '''
<script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script>
<script type="text/javascript" src="/images/js/jquery.tablesorter.pager.js"></script>
<script type="text/javascript" src="/images/js/jquery.confirm.js"></script>
    '''
    add_init = '''
function toggle_new(form_elem_id, link_elem_id) {
    form_elem = document.getElementById(form_elem_id);
    form_focus = document.getElementById(form_elem_id + "_main");
    link_elem = document.getElementById(link_elem_id);
    if (!form_elem || !link_elem)
        return;
    if (form_elem.style.display != 'block') {
        form_elem.style.display = 'block';
        form_focus.focus();
        link_elem.style.display = 'none';
    } else {
        form_elem.style.display = 'none';
        link_elem.style.display = 'block';
        link_elem.focus();
    }
}
    '''
    add_ready = '''
          // init confirmation dialog
          $( "#confirm_dialog" ).dialog(
              // see http://jqueryui.com/docs/dialog/ for options
              { autoOpen: false,
                modal: true, closeOnEscape: true,
                width: 640,
                buttons: {
                   "Cancel": function() { $( "#" + name ).dialog("close"); }
                }
              });

          // table initially sorted by 0 (last update / date) 
          var sortOrder = [[0,1]];

          // use image path for sorting if there is any inside
          var imgTitle = function(contents) {
              var key = $(contents).find("a").attr("class");
              if (key == null) {
                  key = $(contents).html();
              }
              return key;
          }

          $("#forumtable").tablesorter({widgets: ["zebra"],
                                        sortList:sortOrder,
                                        textExtraction: imgTitle
                                        })
                               .tablesorterPager({ container: $("#pager"),
                                        size: %s
                                        });
          $("#pagerrefresh").click(function() { location.reload(); });
    ''' % default_pager_entries
    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':
        '''
 <div id="confirm_dialog" title="Confirm" style="background:#fff;">
  <div id="confirm_text"><!-- filled by js --></div>
   <textarea cols="72" rows="10" id="confirm_input" style="display:none;"></textarea>
 </div>
'''
    })

    output_objects.append({
        'object_type': 'sectionheader',
        'text': '%s Forum for %s' % (label, vgrid_name)
    })

    try:
        os.makedirs(forum_base)
    except:
        pass
    if not os.path.isdir(forum_base):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '''No forum available for %s!''' % vgrid_name
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    post_error = None
    msg = ''

    logger.info("vgridforum %s %s %s" % (vgrid_name, action, thread))

    if action in post_actions:
        if action == 'new_thread':
            try:
                (thread_hash, msg) = new_subject(forum_base, client_id,
                                                 msg_subject, msg_body)
                query = 'vgrid_name=%s&action=show_thread&thread=%s'\
                        % (vgrid_name, thread_hash)
                url = "%s?%s" % (os.environ['SCRIPT_URI'], query)
                notify_subscribers(configuration, forum_base, vgrid_name, '',
                                   client_id, url)
                thread = thread_hash
            except ValueError, error:
                post_error = str(error)
        elif action == 'reply':
            try:
                (thread_hash, msg) = reply(forum_base, client_id, msg_body,
                                           thread)
                query = 'vgrid_name=%s&action=show_thread&thread=%s'\
                        % (vgrid_name, thread_hash)
                url = "%s?%s" % (os.environ['SCRIPT_URI'], query)
                notify_subscribers(configuration, forum_base, vgrid_name, '',
                                   client_id, url)
                notify_subscribers(configuration, forum_base, vgrid_name,
                                   thread_hash, client_id, url)
            except ValueError, error:
                post_error = str(error)
Ejemplo n.º 59
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:
        WARNING_MSG = str(accepted)
        output_objects.append({'object_type': 'warning', 'text': WARNING_MSG})
        return (accepted, returnvalues.CLIENT_ERROR)

    # Convert accpeted values to string and filter out NON-set values

    accepted_joined_values = {
        key: ''.join(value)
        for (key, value) in accepted.iteritems() if len(value) > 0
    }

    action = accepted_joined_values['action']
    flags = accepted_joined_values['flags']
    path = accepted_joined_values['path']
    extension = accepted['extension'][-1].strip()

    logger.debug('%s from %s: %s' % (op_name, client_id, accepted))

    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,
        ):
            logger.info('checkpoint2')
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '''Only accepting
                CSRF-filtered POST requests to prevent unintended updates'''
            })
            return (output_objects, returnvalues.CLIENT_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
    abs_path = os.path.join(base_dir, path)

    title_entry = find_entry(output_objects, 'title')
    title_entry['text'] = 'IMAGEPREVIEW Management'
    output_objects.append({
        'object_type': 'header',
        'text': 'IMAGEPREVIEW Management'
    })
    status = returnvalues.ERROR

    vgrid_name = in_vgrid_share(configuration, abs_path)
    vgrid_owner = vgrid_is_owner(vgrid_name, client_id, configuration)

    status = returnvalues.OK
    if vgrid_name is None:
        status = returnvalues.ERROR
        ERROR_MSG = "No vgrid found for path: '%s'" % path
        output_objects.append({'object_type': 'error_text', 'text': ERROR_MSG})

    if status == returnvalues.OK:
        if action == 'list_settings':
            status = list_settings(configuration, abs_path, path,
                                   output_objects)
            logger.debug('list exit status: %s' % str(status))
        elif action == 'remove_setting':
            if vgrid_owner == False:
                status = returnvalues.ERROR
                ERROR_MSG = \
                    "Ownership of vgrid: '%s' required to change imagepreview settings" \
                    % vgrid_name
                output_objects.append({
                    'object_type': 'error_text',
                    'text': ERROR_MSG
                })
            else:
                status = remove_setting(configuration, abs_path, path,
                                        extension, output_objects)
            logger.debug('remove_setting exit status: %s' % str(status))
        elif action == 'get_setting':
            status = get_setting(configuration, abs_path, path, extension,
                                 output_objects)
            logger.debug('get_setting exit status: %s' % str(status))
        elif action == 'update_setting':
            if vgrid_owner == False:
                status = returnvalues.ERROR
                ERROR_MSG = \
                    "Ownership of vgrid: '%s' required to change imagepreview settings" \
                    % vgrid_name
                output_objects.append({
                    'object_type': 'error_text',
                    'text': ERROR_MSG
                })
            else:
                status = update_setting(
                    configuration,
                    base_dir,
                    abs_path,
                    path,
                    extension,
                    accepted_joined_values,
                    output_objects,
                )
                logger.debug('update_setting exit status: %s' % str(status))
        elif action == 'create_setting':

            if vgrid_owner == False:
                status = returnvalues.ERROR
                ERROR_MSG = \
                    "Ownership of vgrid: '%s' required to change imagepreview settings" \
                    % vgrid_name
                output_objects.append({
                    'object_type': 'error_text',
                    'text': ERROR_MSG
                })
            else:
                status = create_setting(
                    configuration,
                    client_id,
                    base_dir,
                    abs_path,
                    path,
                    extension,
                    accepted_joined_values,
                    output_objects,
                )
                status = returnvalues.OK
            logger.debug('create_setting exit status: %s' % str(status))
        elif action == 'reset_setting':
            if vgrid_owner == False:
                status = returnvalues.ERROR
                ERROR_MSG = \
                    "Ownership of vgrid: '%s' required to change imagepreview settings" \
                    % vgrid_name
                output_objects.append({
                    'object_type': 'error_text',
                    'text': ERROR_MSG
                })
            else:
                status = reset_settings(configuration, abs_path, path,
                                        output_objects, extension)
            logger.debug('reset exit status: %s' % str(status))
        elif action == 'get':

            status = get(configuration, base_dir, path, output_objects)
            logger.debug('get exit status: %s' % str(status))
        elif action == 'remove':
            if vgrid_owner == False:
                status = returnvalues.ERROR
                ERROR_MSG = \
                    "Ownership of vgrid: '%s' required to change imagepreview settings" \
                    % vgrid_name
                output_objects.append({
                    'object_type': 'error_text',
                    'text': ERROR_MSG
                })
            else:
                status = remove(configuration, base_dir, abs_path, path,
                                output_objects)
                logger.debug('remove exit status: %s' % str(status))
        elif action == 'clean':
            if vgrid_owner == False:
                status = returnvalues.ERROR
                ERROR_MSG = \
                    "Ownership of vgrid: '%s' required to change imagepreview settings" \
                    % vgrid_name
                output_objects.append({
                    'object_type': 'error_text',
                    'text': ERROR_MSG
                })
            else:
                status = clean(configuration, base_dir, abs_path, path,
                               output_objects)
                logger.debug('clean exit status: %s' % str(status))
        elif action == 'cleanrecursive':

            if vgrid_owner == False:
                status = returnvalues.ERROR
                ERROR_MSG = \
                    "Ownership of vgrid: '%s' required to change imagepreview settings" \
                    % vgrid_name
                output_objects.append({
                    'object_type': 'error_text',
                    'text': ERROR_MSG
                })
            else:
                status = clean(
                    configuration,
                    base_dir,
                    abs_path,
                    path,
                    output_objects,
                    recursive=True,
                )
                logger.debug('cleanrecursive exit status: %s' % str(status))
        elif action == 'refresh':

            if vgrid_owner == False:
                status = returnvalues.ERROR
                ERROR_MSG = \
                    "Ownership of vgrid: '%s' required to change imagepreview settings" \
                    % vgrid_name
                output_objects.append({
                    'object_type': 'error_text',
                    'text': ERROR_MSG
                })
            else:
                status = refresh(
                    configuration,
                    client_id,
                    base_dir,
                    abs_path,
                    path,
                    output_objects,
                )
                logger.debug('refresh exit status: %s' % str(status))
        else:
            ERROR_MSG = "action: '%s' _NOT_ implemented yet" \
                % str(action)
            output_objects.append({
                'object_type': 'error_text',
                'text': ERROR_MSG
            })

    logger.debug('output_objects: %s' % str(output_objects))
    logger.debug('status: %s' % str(status))
    return (output_objects, status)
Ejemplo n.º 60
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,
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    flags = ''.join(accepted['flags'])
    patterns = accepted['path']

    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)

    # 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
        # NB: Globbing disabled on purpose here

        unfiltered_match = [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':
                'error_text',
                'text':
                "%s: '%s': Permission denied" % (op_name, pattern)
            })
            status = returnvalues.CLIENT_ERROR

        for abs_path in match:
            relative_path = abs_path.replace(base_dir, '')
            if verbose(flags):
                output_objects.append({
                    'object_type': 'file',
                    'name': relative_path
                })
            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 touch "%s": inside a read-only location!' % \
                     pattern})
                status = returnvalues.CLIENT_ERROR
                continue

            try:
                # Create file if missing
                if not os.path.exists(abs_path):
                    os.close(os.open(abs_path, os.O_WRONLY | os.O_CREAT, 0666))
                os.utime(abs_path, None)
                logger.info('%s %s done' % (op_name, abs_path))
            except Exception, 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