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, op_header=False)
    defaults = signature()[1]
    title_entry = find_entry(output_objects, 'title')
    label = "%s" % configuration.site_vgrid_label
    title_entry['text'] = "Remove %s Trigger" % label
    output_objects.append({'object_type': 'header', 'text'
                          : 'Remove %s Trigger' % label})
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
        )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

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

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

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

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

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

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

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

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

    # can't remove if not a participant

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

    # remove

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

    logger.info('%s removed trigger: %s' % (client_id, rule_id))
    output_objects.append({'object_type': 'text', 'text':
                           'Trigger %s successfully removed from %s %s!'
                           % (rule_id, vgrid_name, label)})
    output_objects.append({'object_type': 'link', 'destination':
                           'vgridworkflows.py?vgrid_name=%s' % vgrid_name,
                           'text': 'Back to workflows for %s' % vgrid_name})
    return (output_objects, returnvalues.OK)
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)
    defaults = signature()[1]
    title_entry = find_entry(output_objects, 'title')
    label = "%s" % configuration.site_vgrid_label
    title_entry['text'] = "Add %s Resource" % label
    output_objects.append({
        'object_type': 'header',
        'text': 'Add %s Resource(s)' % label
    })
    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)

    vgrid_name = accepted['vgrid_name'][-1].strip()
    res_id_list = accepted['unique_resource_name']
    request_name = unhexlify(accepted['request_name'][-1])
    rank_list = accepted['rank'] + ['' for _ in res_id_list]

    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)

    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 resources

    (allow_status, allow_msg) = allow_resources_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)

    res_id_added = []
    for (res_id, rank_str) in zip(res_id_list, rank_list):
        unique_resource_name = res_id.lower().strip()
        try:
            rank = int(rank_str)
        except ValueError:
            rank = None

        # 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,
                                      unique_resource_name, 'resource',
                                      configuration)
        if not ret_val:
            output_objects.append({'object_type': 'error_text', 'text': msg})
            status = returnvalues.CLIENT_ERROR
            continue
        elif msg:

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

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

        # don't add if already in vgrid or parent vgrid unless rank is given

        if rank is None and vgrid_is_resource(vgrid_name, unique_resource_name,
                                              configuration):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '%s is already a resource in the %s' %
                (unique_resource_name, label)
            })
            status = returnvalues.CLIENT_ERROR
            continue

        # don't add if already in 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)
            })
            status = returnvalues.SYSTEM_ERROR
            continue
        skip_entity = False
        for subvgrid in subvgrids:
            if vgrid_is_resource(subvgrid,
                                 unique_resource_name,
                                 configuration,
                                 recursive=False):
                output_objects.append({
                    'object_type': 'error_text',
                    'text': '''%(res_name)s is already in a
sub-%(vgrid_label)s (%(subvgrid)s). Please remove the resource from the
sub-%(vgrid_label)s and try again''' % {
                        'res_name': unique_resource_name,
                        'subvgrid': subvgrid,
                        'vgrid_label': label
                    }
                })
                status = returnvalues.CLIENT_ERROR
                skip_entity = True
                break
        if skip_entity:
            continue

        # Check if only rank change was requested and apply if so

        if rank is not None:
            (add_status, add_msg) = vgrid_add_resources(configuration,
                                                        vgrid_name,
                                                        [unique_resource_name],
                                                        rank=rank)
            if not add_status:
                output_objects.append({
                    'object_type': 'error_text',
                    'text': add_msg
                })
                status = returnvalues.SYSTEM_ERROR
            else:
                output_objects.append({
                    'object_type':
                    'text',
                    'text':
                    'changed %s to resource %d' % (res_id, rank)
                })
            # No further action after rank change as everything else exists
            continue

        # Getting here means res_id is neither resource 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(configuration.vgrid_home + os.sep +
                                   vgrid_name) + os.sep
        resources_file = base_dir + 'resources'

        # Add to list and pickle

        (add_status, add_msg) = vgrid_add_resources(configuration, vgrid_name,
                                                    [unique_resource_name])
        if not add_status:
            output_objects.append({
                'object_type': 'error_text',
                'text': '%s' % add_msg
            })
            status = returnvalues.SYSTEM_ERROR
            continue
        res_id_added.append(unique_resource_name)

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

    if res_id_added:
        output_objects.append({
            'object_type':
            'html_form',
            'text':
            'New resource(s)<br />%s<br />successfully added to %s %s!'
            '' % ('<br />'.join(res_id_added), vgrid_name, label)
        })
        res_id_fields = ''
        for res_id in res_id_added:
            res_id_fields += """
<input type=hidden name=unique_resource_name value='%s' />""" % res_id

        form_method = 'post'
        csrf_limit = get_csrf_limit(configuration)
        fill_helpers = {
            'vgrid_name': vgrid_name,
            'unique_resource_name': unique_resource_name,
            'protocol': any_protocol,
            'short_title': configuration.short_title,
            'vgrid_label': label,
            'res_id_fields': res_id_fields,
            'form_method': form_method,
            'csrf_field': csrf_field,
            'csrf_limit': csrf_limit
        }
        target_op = 'sendrequestaction'
        csrf_token = make_csrf_token(configuration, form_method, target_op,
                                     client_id, csrf_limit)
        fill_helpers.update({'target_op': target_op, 'csrf_token': csrf_token})
        output_objects.append({
            'object_type':
            'html_form',
            'text':
            """
<form method='%(form_method)s' action='%(target_op)s.py'>
<input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
<input type=hidden name=request_type value='vgridaccept' />
<input type=hidden name=vgrid_name value='%(vgrid_name)s' />
%(res_id_fields)s
<input type=hidden name=protocol value='%(protocol)s' />
<table>
<tr>
<td class='title'>Custom message to resource owners</td>
</tr><tr>
<td><textarea name=request_text cols=72 rows=10>
We have granted your %(unique_resource_name)s resource access to our
%(vgrid_name)s %(vgrid_label)s.
You can assign it to accept jobs from the %(vgrid_name)s %(vgrid_label)s from
your Resources page on %(short_title)s.

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

    output_objects.append({
        'object_type': 'link',
        'destination': 'adminvgrid.py?vgrid_name=%s' % vgrid_name,
        'text': 'Back to administration for %s' % vgrid_name
    })
    return (output_objects, returnvalues.OK)
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)
    defaults = signature()[1]
    title_entry = find_entry(output_objects, 'title')
    label = "%s" % configuration.site_vgrid_label
    title_entry['text'] = "Reject %s Request" % label
    output_objects.append({
        'object_type': 'header',
        'text': 'Reject %s Request' % label
    })
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

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

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

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

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

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

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

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

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

Regards, the %(target)s %(vgrid_label)s owners
</textarea></td>
</tr>
<tr>
<td><input type='submit' value='Inform requestor(s)' /></td>
</tr>
</table>
</form>
<br />
""" % fill_helpers
    })
    output_objects.append({
        'object_type': 'link',
        'destination': 'adminvgrid.py?vgrid_name=%s' % vgrid_name,
        'text': 'Back to administration for %s' % vgrid_name
    })
    return (output_objects, returnvalues.OK)
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)
    client_dir = client_id_dir(client_id)
    defaults = signature()[1]
    title_entry = find_entry(output_objects, 'title')
    label = "%s" % configuration.site_vgrid_label
    title_entry['text'] = "Add/Update %s Trigger" % label
    output_objects.append({
        'object_type': 'header',
        'text': 'Add/Update %s Trigger' % label
    })
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
        typecheck_overrides={'path': valid_path_pattern},
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    # NOTE: strip leftmost slashes from all fields used in file paths to avoid
    # interference with os.path.join calls. Furthermore we strip and normalize
    # the path variable first to make sure it does not point outside the vgrid.
    # In practice any such directory traversal attempts will generally be moot
    # since the grid_events daemon only starts a listener for each top-level
    # vgrid and in there only reacts to events that match trigger rules from
    # that particular vgrid. Thus only subvgrid access to parent vgrids might
    # be a concern and still of limited consequence.
    # NOTE: merge multi args into one string and split again to get flat array
    rule_id = accepted['rule_id'][-1].strip()
    vgrid_name = accepted['vgrid_name'][-1].strip().lstrip(os.sep)
    path = os.path.normpath(accepted['path'][-1].strip()).lstrip(os.sep)
    changes = [i.strip() for i in ' '.join(accepted['changes']).split()]
    action = accepted['action'][-1].strip()
    arguments = [
        i.strip() for i in shlex.split(' '.join(accepted['arguments']))
    ]
    rate_limit = accepted['rate_limit'][-1].strip()
    settle_time = accepted['settle_time'][-1].strip()
    match_files = accepted['match_files'][-1].strip() == 'True'
    match_dirs = accepted['match_dirs'][-1].strip() == 'True'
    match_recursive = accepted['match_recursive'][-1].strip() == 'True'
    rank_str = accepted['rank'][-1]
    try:
        rank = int(rank_str)
    except ValueError:
        rank = None

    logger.debug("addvgridtrigger with args: %s" % user_arguments_dict)

    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

    # we just use a high res timestamp as automatic rule_id

    if rule_id == keyword_auto:
        rule_id = "%d" % (time.time() * 1E8)

    if action == keyword_auto:
        action = valid_trigger_actions[0]

    if any_state in changes:
        changes = valid_trigger_changes

    logger.info("addvgridtrigger %s" % 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,
                                  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 allowed to add rule

    # don't add if already in vgrid or parent vgrid - but update if owner

    update_id = None
    if vgrid_is_trigger(vgrid_name, rule_id, configuration):
        if vgrid_is_trigger_owner(vgrid_name, rule_id, client_id,
                                  configuration):
            update_id = 'rule_id'
        else:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '%s is already a trigger owned by somebody else in the %s' %
                (rule_id, label)
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

    # don't add if already in 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)
    for subvgrid in subvgrids:
        if vgrid_is_trigger(subvgrid, rule_id, configuration, recursive=False):
            output_objects.append({
                'object_type': 'error_text',
                'text': '''%(rule_id)s is already in a
sub-%(vgrid_label)s (%(subvgrid)s). Please remove the trigger from the
sub-%(vgrid_label)s and try again''' % {
                    'rule_id': rule_id,
                    'subvgrid': subvgrid,
                    'vgrid_label': label
                }
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

    if not action in valid_trigger_actions:
        output_objects.append({
            'object_type': 'error_text',
            'text': "invalid action value %s" % action
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    if keyword_all in changes:
        changes = valid_trigger_changes
    for change in changes:
        if not change in valid_trigger_changes:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                "found invalid change value %s" % change
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

    # Check if we should load saved trigger for rank change or update

    rule_dict = None
    if rank is not None or update_id is not None:
        (load_status, all_triggers) = vgrid_triggers(vgrid_name, configuration)
        if not load_status:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Failed to load triggers for %s: %s' %
                (vgrid_name, all_triggers)
            })
            return (output_objects, returnvalues.SYSTEM_ERROR)
        for saved_dict in all_triggers:
            if saved_dict['rule_id'] == rule_id:
                rule_dict = saved_dict
                break
        if rule_dict is None:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'No such trigger %s for %s: %s' %
                (rule_id, vgrid_name, all_triggers)
            })
            return (output_objects, returnvalues.CLIENT_ERROR)
    elif not path:
        # New trigger with missing path
        output_objects.append({
            'object_type': 'error_text',
            'text': '''Either path or rank must
be set.'''
        })
        return (output_objects, returnvalues.CLIENT_ERROR)
    elif action == "submit" and not arguments:
        # New submit trigger with missing mrsl arguments
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '''Submit triggers must give
a job description file path as argument.'''
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Handle create and update (i.e. new, update all or just refresh mRSL)

    if rank is None:

        # IMPORTANT: we save the job template contents to avoid potential abuse
        # Otherwise someone else in the VGrid could tamper with the template
        # and make the next trigger execute arbitrary code on behalf of the
        # rule owner.

        templates = []

        # Merge current and saved values

        req_dict = {
            'rule_id': rule_id,
            'vgrid_name': vgrid_name,
            'path': path,
            'changes': changes,
            'run_as': client_id,
            'action': action,
            'arguments': arguments,
            'rate_limit': rate_limit,
            'settle_time': settle_time,
            'match_files': match_files,
            'match_dirs': match_dirs,
            'match_recursive': match_recursive,
            'templates': templates
        }
        if rule_dict is None:
            rule_dict = req_dict
        else:
            for field in user_arguments_dict:
                if field in req_dict:
                    rule_dict[field] = req_dict[field]

        # Now refresh template contents

        if rule_dict['action'] == "submit":
            for rel_path in rule_dict['arguments']:
                # IMPORTANT: path must be expanded to abs for proper chrooting
                abs_path = os.path.abspath(os.path.join(base_dir, rel_path))
                try:
                    if not valid_user_path(configuration, abs_path, base_dir,
                                           True):
                        logger.warning(
                            '%s tried to %s restricted path %s ! (%s)' %
                            (client_id, op_name, abs_path, rel_path))
                        raise ValueError('invalid submit path argument: %s' %
                                         rel_path)
                    temp_fd = open(abs_path)
                    templates.append(temp_fd.read())
                    temp_fd.close()
                except Exception as err:
                    logger.error("read submit argument file failed: %s" % err)
                    output_objects.append({
                        'object_type':
                        'error_text',
                        'text':
                        'failed to read submit argument file "%s"' % rel_path
                    })
                    return (output_objects, returnvalues.CLIENT_ERROR)

        # Save updated template contents here
        rule_dict['templates'] = templates

    # Add to list and pickle

    (add_status, add_msg) = vgrid_add_triggers(configuration, vgrid_name,
                                               [rule_dict], update_id, rank)
    if not add_status:
        logger.error('%s failed to add/update trigger: %s' %
                     (client_id, add_msg))
        output_objects.append({
            'object_type': 'error_text',
            'text': '%s' % add_msg
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    if rank is not None:
        logger.info('%s moved trigger %s to %d' % (client_id, rule_id, rank))
        output_objects.append({
            'object_type':
            'text',
            'text':
            'moved %s trigger %s to position %d' % (vgrid_name, rule_id, rank)
        })
    elif update_id:
        logger.info('%s updated trigger: %s' % (client_id, rule_dict))
        output_objects.append({
            'object_type':
            'text',
            'text':
            'Existing trigger %s successfully updated in %s %s!' %
            (rule_id, vgrid_name, label)
        })
    else:
        logger.info('%s added new trigger: %s' % (client_id, rule_dict))
        output_objects.append({
            'object_type':
            'text',
            'text':
            'New trigger %s successfully added to %s %s!' %
            (rule_id, vgrid_name, label)
        })

    output_objects.append({
        'object_type': 'link',
        'destination': 'vgridworkflows.py?vgrid_name=%s' % vgrid_name,
        'text': 'Back to workflows for %s' % vgrid_name
    })
    return (output_objects, returnvalues.OK)
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)
    defaults = signature()[1]
    title_entry = find_entry(output_objects, 'title')
    label = "%s" % configuration.site_vgrid_label
    title_entry['text'] = "Add %s Owner" % label
    output_objects.append({
        'object_type': 'header',
        'text': 'Add %s Owner(s)' % label
    })
    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)

    vgrid_name = accepted['vgrid_name'][-1].strip()
    cert_id_list = accepted['cert_id']
    request_name = unhexlify(accepted['request_name'][-1])
    rank_list = accepted['rank'] + ['' for _ in cert_id_list]
    # inherited vgrid membership
    inherit_vgrid_member = False

    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)

    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 owners

    (allow_status, allow_msg) = allow_owners_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)

    cert_id_added = []
    for (cert_id, rank_str) in zip(cert_id_list, rank_list):
        cert_id = cert_id.strip()
        cert_dir = client_id_dir(cert_id)
        try:
            rank = int(rank_str)
        except ValueError:
            rank = None

        # 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})
            status = returnvalues.CLIENT_ERROR
            continue

        # don't add if already an owner unless rank is given

        if rank is None and 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, label)
            })
            status = returnvalues.CLIENT_ERROR
            continue

        # 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)
            })
            status = returnvalues.CLIENT_ERROR
            continue

        # 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)
            })
            status = returnvalues.SYSTEM_ERROR
            continue

        skip_entity = False
        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, label, subvgrid)
                })
                status = returnvalues.CLIENT_ERROR
                skip_entity = True
                break
            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, label, subvgrid)
                })
                status = returnvalues.CLIENT_ERROR
                skip_entity = True
                break
        if skip_entity:
            continue

        # 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, label, inherit_vgrid_member)
            })

        # Check if only rank change was requested and apply if so

        if rank is not None:
            (add_status, add_msg) = vgrid_add_owners(configuration,
                                                     vgrid_name, [cert_id],
                                                     rank=rank)
            if not add_status:
                output_objects.append({
                    'object_type': 'error_text',
                    'text': add_msg
                })
                status = returnvalues.SYSTEM_ERROR
            else:
                output_objects.append({
                    'object_type':
                    'text',
                    'text':
                    'changed %s to owner %d' % (cert_id, rank)
                })
            # No further action after rank change as everything else exists
            continue

        # 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
            })
            status = returnvalues.CLIENT_ERROR
            continue

        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!'''
            })
            status = returnvalues.CLIENT_ERROR
            continue

        # 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!'''
            })
            status = returnvalues.CLIENT_ERROR
            continue

        # 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
            })
            status = returnvalues.SYSTEM_ERROR
            continue

        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 as exc:
            pass

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

        try:
            os.mkdir(user_private_base)
        except Exception as exc:
            pass

        if is_subvgrid:
            share_dir = None
            try:

                # Example:
                #    vgrid_name = IMADA/STUD/BACH
                #    vgrid_name_last_fragment = BACH
                #    vgrid_name_without_last_fragment = IMADA/STUD/

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

                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

                share_dir = user_dir + vgrid_name_without_last_fragment
                if not os.path.isdir(share_dir):
                    os.makedirs(share_dir)
                pub_dir = user_public_base + vgrid_name_without_last_fragment
                if not os.path.isdir(pub_dir):
                    os.makedirs(pub_dir)
                priv_dir = user_private_base + vgrid_name_without_last_fragment
                if not os.path.isdir(priv_dir):
                    os.makedirs(priv_dir)
            except Exception as 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, share_dir))
                status = returnvalues.SYSTEM_ERROR
                continue

        # create symlink from users home directory to vgrid file directory
        # unless member of parent vgrid so that it is included already

        link_src = os.path.abspath(configuration.vgrid_files_home + os.sep +
                                   vgrid_name) + os.sep
        link_dst = user_dir + vgrid_name

        if not inherit_vgrid_member and \
                not make_symlink(link_src, link_dst, logger):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Could not create link to %s share!' % label
            })
            logger.error('Could not create link to %s files (%s -> %s)' %
                         (label, link_src, link_dst))
            status = returnvalues.SYSTEM_ERROR
            continue

        public_base_dst = user_public_base + vgrid_name

        # create symlink for public_base files

        if not make_symlink(public_base_dir, public_base_dst, logger):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Could not create link to public_base dir!'
            })
            logger.error(
                'Could not create link to public_base dir (%s -> %s)' %
                (public_base_dir, public_base_dst))
            status = returnvalues.SYSTEM_ERROR
            continue

        private_base_dst = user_private_base + vgrid_name

        # create symlink for private_base files

        if not make_symlink(private_base_dir, private_base_dst, logger):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Could not create link to private_base dir!'
            })
            status = returnvalues.SYSTEM_ERROR
            continue

        if configuration.trac_admin_path:
            public_tracker_dir = \
                os.path.abspath(os.path.join(
                    configuration.vgrid_public_base, vgrid_name, '.vgridtracker'))
            private_tracker_dir = \
                os.path.abspath(os.path.join(
                    configuration.vgrid_private_base, vgrid_name, '.vgridtracker'))
            vgrid_tracker_dir = \
                os.path.abspath(os.path.join(
                    configuration.vgrid_files_home, vgrid_name, '.vgridtracker'))
            for tracker_dir in [
                    public_tracker_dir, private_tracker_dir, vgrid_tracker_dir
            ]:
                if not add_tracker_admin(configuration, cert_id, vgrid_name,
                                         tracker_dir, output_objects):
                    status = returnvalues.SYSTEM_ERROR
                    continue
        cert_id_added.append(cert_id)

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

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

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

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

    output_objects.append({
        'object_type': 'link',
        'destination': 'adminvgrid.py?vgrid_name=%s' % vgrid_name,
        'text': 'Back to administration for %s' % vgrid_name
    })
    return (output_objects, status)
Ejemplo n.º 6
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id, op_header=False)
    defaults = signature()[1]
    title_entry = find_entry(output_objects, 'title')
    label = "%s" % configuration.site_vgrid_label
    title_entry['text'] = "Remove %s Resource" % label
    output_objects.append({
        'object_type': 'header',
        'text': 'Remove %s Resource' % 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]
    unique_resource_name = accepted['unique_resource_name'][-1].lower()

    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)

    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 resources

    (allow_status, allow_msg) = allow_resources_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, ret_variables) = \
        init_vgrid_script_add_rem(vgrid_name, client_id,
                                  unique_resource_name, 'resource',
                                  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 not vgrid_is_owner(vgrid_name, client_id, configuration):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '''You must be an owner of the %s to
remove a resource!''' % label
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # don't remove if not a participant

    if not vgrid_is_resource(vgrid_name, unique_resource_name, configuration):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '%s is not a resource in %s or a parent %s.' %
            (unique_resource_name, vgrid_name, label)
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # remove

    (rm_status, rm_msg) = vgrid_remove_resources(configuration, vgrid_name,
                                                 [unique_resource_name])
    if not rm_status:
        output_objects.append({'object_type': 'error_text', 'text': rm_msg})
        output_objects.append({
            'object_type': 'error_text',
            'text': '''%(res_name)s might be listed as a resource
of this %(vgrid_label)s because it is a resource of a parent %(vgrid_label)s.
Removal must be performed from the most significant %(vgrid_label)s possible.
''' % {
                'res_name': unique_resource_name,
                'vgrid_label': label
            }
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    output_objects.append({
        'object_type':
        'text',
        'text':
        'Resource %s successfully removed from %s %s!' %
        (unique_resource_name, 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.º 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, 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 as exc:

        # ouch, not good. Email admin?

        pass

    if os.path.exists(dst):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'Could not remove link to %s files!' % label
        })
        logger.error('Removed member might still have access to %s files! %s' %
                     (label, dst))
        return (output_objects, returnvalues.SYSTEM_ERROR)

    vgrid_name_parts = vgrid_name.split('/')

    # make sure there are no "" entries in list

    while True:
        try:
            vgrid_name_parts.remove('')
            vgrid_name_parts.remove('/')
        except:

            # no such item

            break

    is_subvgrid = len(vgrid_name_parts) >= 2
    if is_subvgrid:

        # remove placeholder dirs (empty dirs created to hold subvgrid)

        # reverse list to remove files and directories of subdirs first

        list_range = range(len(vgrid_name_parts))
        list_range.reverse()
        reverse_list = list_range

        # remove first entry in reversed list (SUBVGRID in VGRID/SUBVGRID since
        # we know it was the symbolic link and is not a dir)

        reverse_list = reverse_list[1:]

        # remove empty placeholder dirs in home dir, private_base and
        # public_base dirs

        base_dirs = [user_dir]
        for base_dir in base_dirs:
            for loop_count in reverse_list:

                # note that loop_count is decreasing!

                current_vgrid_path = \
                    '/'.join(vgrid_name_parts[0:loop_count + 1])
                current_path = base_dir + current_vgrid_path
                if not os.path.isdir(current_path):
                    output_objects.append({
                        'object_type':
                        'error_text',
                        'text':
                        '''Error removing %s placeholder dirs:
%s is not a directory, not going to remove.''' % (label, current_vgrid_path)
                    })
                    continue

                if os.listdir(current_path):
                    output_objects.append({
                        'object_type':
                        'error_text',
                        'text':
                        '''Could not remove %s placeholder dirs:
%s is not an empty directory (not critical)''' % (label, current_vgrid_path)
                    })
                else:

                    # remove empty directory

                    try:
                        os.rmdir(current_path)
                    except Exception as exc:
                        output_objects.append({
                            'object_type':
                            'error_text',
                            'text':
                            '''Error removing %s placeholder dirs:
exception removing empty directory %s''' % (label, exc)
                        })
                        return (output_objects, returnvalues.SYSTEM_ERROR)

    # remove from list

    (rm_status, rm_msg) = vgrid_remove_members(configuration, vgrid_name,
                                               [cert_id])
    if not rm_status:
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '%s of member of %s' % (rm_msg, vgrid_name)
        })
        output_objects.append({
            'object_type': 'error_text',
            'text': '''(If %(vgrid_label)s %(vgrid_name)s has
sub-%(vgrid_label)ss then removal must be performed from the most significant
%(vgrid_label)s possible.)''' % {
                'vgrid_name': vgrid_name,
                'vgrid_label': label
            }
        })
        return (output_objects, returnvalues.SYSTEM_ERROR)

    unmap_inheritance(configuration, vgrid_name, cert_id)

    output_objects.append({
        'object_type':
        'text',
        'text':
        '%s successfully removed as member of %s %s!' %
        (cert_id, 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.º 8
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id, op_header=False)
    defaults = signature()[1]
    title_entry = find_entry(output_objects, 'title')
    label = "%s" % configuration.site_vgrid_label
    title_entry['text'] = "Remove %s Owner" % label
    output_objects.append({
        'object_type': 'header',
        'text': 'Remove %s Owner' % 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]
    flags = ''.join(accepted['flags'])
    cert_id = accepted['cert_id'][-1]
    cert_dir = client_id_dir(cert_id)
    # inherited vgrid membership
    inherit_vgrid_member = False

    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 owner 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 owners
        (allow_status, allow_msg) = allow_owners_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,
                                  'owner', 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 already an owner

    if not vgrid_is_owner(vgrid_name, cert_id, configuration):
        logger.warning('%s is not allowed to remove owner %s from %s' %
                       (client_id, cert_id, vgrid_name))
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '%s is not an owner of %s or a parent %s.' %
            (cert_id, vgrid_name, label)
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # we need the local owners file to detect inherited ownerships

    (owners_status, owners_direct) = vgrid_owners(vgrid_name, configuration,
                                                  False)
    (all_status, owners) = vgrid_owners(vgrid_name, configuration, True)
    if not owners_status or not all_status:
        logger.error('Error loading owners for %s: %s / %s' %
                     (vgrid_name, owners_direct, owners))
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'An internal error occurred, error conditions have been logged.'
        })
        output_objects.append({
            'object_type':
            'text',
            'text':
            '''
         You can help us fix the problem by notifying the administrators
         via mail about what you wanted to do when the error happened.'''
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    logger.info('%s removing owner %s from %s' %
                (client_id, cert_id, vgrid_name))

    # find out whether to just remove an owner or delete the whole thing.
    # ask about delete if last or no direct owners.

    if len(owners_direct) > 1:

        logger.debug('Removing %s, one of several owners, from %s.' %
                     (cert_id, vgrid_name))

        if not (cert_id in owners_direct):

            # the owner owns an upper vgrid, ownership is inherited
            # cannot remove, not last (inherited) owner

            logger.warning('Cannot delete: Inherited ownership.' +
                           '\n Owners: %s,\n Direct owners: %s.' %
                           (owners, owners_direct))
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '''%s is owner of a parent %s. 
Owner removal has to be performed at the topmost vgrid''' % (cert_id, label)
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

        else:

            # Remove any tracker admin rights

            if configuration.trac_admin_path:
                public_tracker_dir = \
                    os.path.abspath(os.path.join(
                        configuration.vgrid_public_base, vgrid_name,
                        '.vgridtracker'))
                private_tracker_dir = \
                    os.path.abspath(os.path.join(
                        configuration.vgrid_private_base, vgrid_name,
                        '.vgridtracker'))
                vgrid_tracker_dir = \
                    os.path.abspath(os.path.join(
                        configuration.vgrid_files_home, vgrid_name,
                        '.vgridtracker'))
                for tracker_dir in [
                        public_tracker_dir, private_tracker_dir,
                        vgrid_tracker_dir
                ]:
                    if not rm_tracker_admin(configuration, cert_id, vgrid_name,
                                            tracker_dir, output_objects):
                        return (output_objects, returnvalues.SYSTEM_ERROR)

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

            # Do not touch vgrid share if still a member of a parent vgrid

            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 still a member of parent %s %s.
Preserving access to corresponding %s.''' %
                    (cert_id, label, inherit_vgrid_member, label)
                })
            else:
                (success, msg) = unlink_share(user_dir, vgrid_name)
                if not success:
                    logger.error('Could not remove share link: %s.' % msg)
                    output_objects.append({
                        'object_type':
                        'error_text',
                        'text':
                        'Could not remove share links: %s.' % msg
                    })
                    return (output_objects, returnvalues.SYSTEM_ERROR)

            # unlink shared web folders

            (success, msg) = unlink_web_folders(user_dir, vgrid_name)
            if not success:
                logger.error('Could not remove web links: %s.' % msg)
                output_objects.append({
                    'object_type':
                    'error_text',
                    'text':
                    'Could not remove web links: %s.' % msg
                })
                return (output_objects, returnvalues.SYSTEM_ERROR)

            # remove user from saved owners list
            (rm_status, rm_msg) = vgrid_remove_owners(configuration,
                                                      vgrid_name, [cert_id])
            if not rm_status:
                output_objects.append({
                    'object_type':
                    'error_text',
                    'text':
                    '%s of owners of %s' % (rm_msg, vgrid_name)
                })
                return (output_objects, returnvalues.SYSTEM_ERROR)

            # Any parent vgrid membership is left untouched here as we only
            # force a normal refresh in unmap_inheritance
            unmap_inheritance(configuration, vgrid_name, cert_id)

            output_objects.append({
                'object_type':
                'text',
                'text':
                '%s successfully removed as owner of %s!' %
                (cert_id, vgrid_name)
            })
            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)

    else:

        # no more direct owners - we try to remove this VGrid

        logger.debug('Leave %s from %s with no more direct owners: delete' %
                     (vgrid_name, cert_id))

        if not force(flags):
            output_objects.append({
                'object_type':
                'text',
                'text':
                '''
No more direct owners of %s - leaving will result in the %s getting
deleted. Please use either of the links below to confirm or cancel.
''' % (vgrid_name, label)
            })
            # Reuse csrf token from this request
            target_op = 'rmvgridowner'
            js_name = target_op
            csrf_token = accepted[csrf_field][-1]
            helper = html_post_helper(
                js_name, '%s.py' % target_op, {
                    'vgrid_name': vgrid_name,
                    'cert_id': cert_id,
                    'flags': 'f',
                    csrf_field: csrf_token
                })
            output_objects.append({'object_type': 'html_form', 'text': helper})
            output_objects.append({
                'object_type':
                'link',
                'destination':
                "javascript: %s();" % js_name,
                'class':
                'removelink iconspace',
                'text':
                'Really leave and delete %s' % vgrid_name
            })
            output_objects.append({'object_type': 'text', 'text': ''})
            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)

        # check if any resources participate or sub-vgrids depend on this one

        (list_status, subs) = vgrid_list_subvgrids(vgrid_name, configuration)

        if not list_status:
            logger.error('Error loading sub-vgrid for %s: %s)' %
                         (vgrid_name, subs))
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '''
An internal error occurred, error conditions have been logged.'''
            })
            output_objects.append({
                'object_type':
                'text',
                'text':
                '''
You can help us fix the problem by notifying the administrators
via mail about what you wanted to do when the error happened.'''
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

        if len(subs) > 0:
            logger.debug('Cannot delete: still has sub-vgrids: %s' % subs)
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '%s has one or more child %ss and cannot be deleted.' %
                (vgrid_name, label)
            })
            output_objects.append({
                'object_type':
                'text',
                'text':
                '''To leave (and delete) %s
first remove all its children: %s.''' % (vgrid_name, ', '.join(subs))
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

        # we consider the local members and resources here, not inherited ones

        (member_status,
         members_direct) = vgrid_members(vgrid_name, configuration, False)
        (resource_status,
         resources_direct) = vgrid_resources(vgrid_name, configuration, False)
        if not member_status or not resource_status:
            logger.warning('failed to load %s members or resources: %s %s' %
                           (vgrid_name, members_direct, resources_direct))
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'could not load %s members or resources for %s.' %
                (label, vgrid_name)
            })
            return (output_objects, returnvalues.SYSTEM_ERROR)
        if len(resources_direct) > 0:
            logger.debug('Cannot delete: still has direct resources %s.' %
                         resources_direct)
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '%s still has resources and cannot be deleted.' % vgrid_name
            })
            output_objects.append({
                'object_type':
                'text',
                'text':
                '''
To leave (and delete) %s, first remove the participating resources.''' %
                vgrid_name
            })

            return (output_objects, returnvalues.CLIENT_ERROR)

        if len(members_direct) > 0:

            logger.debug('Cannot delete: still has direct members %s.' %
                         members_direct)
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '%s still has members and cannot be deleted.' % vgrid_name
            })
            output_objects.append({
                'object_type':
                'text',
                'text':
                '''
To leave (and delete) %s, first remove all members.''' % vgrid_name
            })

            return (output_objects, returnvalues.CLIENT_ERROR)

        # Deleting write restricted VGrid is not allowed

        (load_status, saved_settings) = vgrid_settings(vgrid_name,
                                                       configuration,
                                                       recursive=True,
                                                       as_dict=True)
        if not load_status:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'failed to load saved %s settings' % vgrid_name
            })
            return (output_objects, returnvalues.SYSTEM_ERROR)

        if saved_settings.get('write_shared_files', keyword_members) != \
                keyword_members:
            logger.warning("%s can't delete vgrid %s - write limited!" %
                           (client_id, vgrid_name))
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                """You can't delete
write-restricted %ss - first remove any write restrictions for shared files
on the admin page and then try again.""" % label
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

        # When reaching here, OK to remove the VGrid.
        #   if top-level: unlink, remove all files and directories,
        #   in all cases: remove configuration entry for the VGrid
        #   unlink and move new-style vgrid sub dir to parent

        logger.info('Deleting %s and all related data as requested by %s' %
                    (vgrid_name, cert_id))

        if (cert_id in owners_direct):

            # owner owns this vgrid, direct ownership

            logger.debug('%s looks like a top-level vgrid.' % vgrid_name)
            logger.debug('Deleting all related files.')

            user_dir = os.path.abspath(
                os.path.join(configuration.user_home, cert_dir)) + os.sep
            (share_lnk, share_msg) = unlink_share(user_dir, vgrid_name)
            (web_lnk, web_msg) = unlink_web_folders(user_dir, vgrid_name)
            (files_act,
             files_msg) = abandon_vgrid_files(vgrid_name, configuration)
        else:

            # owner owns some parent vgrid - ownership is only inherited

            logger.debug('%s looks like a sub-vgrid, ownership inherited.' %
                         vgrid_name)
            logger.debug('Only removing entry, leaving files in place.')
            share_lnk, share_msg = True, ''
            web_lnk, web_msg = True, ''
            (files_act,
             files_msg) = inherit_vgrid_files(vgrid_name, configuration)

        (removed, entry_msg) = remove_vgrid_entry(vgrid_name, configuration)

        output_objects.append({
            'object_type':
            'text',
            'text':
            '%s has been removed with last owner.' % vgrid_name
        })

        output_objects.append({
            'object_type': 'link',
            'destination': 'vgridman.py',
            'text': 'Back to the overview.'
        })

        if not share_lnk or not web_lnk or not files_act or not removed:
            err = '\n'.join([share_msg, web_msg, files_msg, entry_msg])
            logger.error('Errors while removing %s:\n%s.' % (vgrid_name, err))

            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '''
An internal error occurred, error conditions have been logged.'''
            })
            output_objects.append({
                'object_type':
                'text',
                'text':
                '''
You can help us fix the problem by notifying the administrators
via mail about what you wanted to do when the error happened.'''
            })
            return (output_objects, returnvalues.CLIENT_ERROR)

        else:

            # Remove vgrid from vgrid cache (after deleting all)
            unmap_vgrid(configuration, vgrid_name)
            return (output_objects, returnvalues.OK)