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)
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)
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)
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
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False, op_menu=False) defaults = signature()[1] (validate_status, accepted) = validate_input(user_arguments_dict, defaults, output_objects, allow_rejects=False) if not validate_status: logger.warning('%s invalid input: %s' % (op_name, accepted)) 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) title_entry = find_entry(output_objects, 'title') title_entry['text'] = '%s certificate request' % configuration.short_title title_entry['skipmenu'] = True output_objects.append({'object_type': 'header', 'text' : '%s certificate request' % \ configuration.short_title }) admin_email = configuration.admin_email smtp_server = configuration.smtp_server user_pending = os.path.abspath(configuration.user_pending) # force name to capitalized form (henrik karlsen -> Henrik Karlsen) # please note that we get utf8 coded bytes here and title() treats such # chars as word termination. Temporarily force to unicode. raw_name = accepted['cert_name'][-1].strip() try: cert_name = force_utf8(force_unicode(raw_name).title()) except Exception: cert_name = raw_name.title() country = accepted['country'][-1].strip().upper() state = accepted['state'][-1].strip().title() org = accepted['org'][-1].strip() # lower case email address email = accepted['email'][-1].strip().lower() password = accepted['password'][-1] verifypassword = accepted['verifypassword'][-1] # keep comment to a single line comment = accepted['comment'][-1].replace('\n', ' ') # single quotes break command line format - remove comment = comment.replace("'", ' ') if password != verifypassword: output_objects.append({'object_type': 'error_text', 'text' : 'Password and verify password are not identical!' }) return (output_objects, returnvalues.CLIENT_ERROR) # TODO: move this check to conf? if not forced_org_email_match(org, email, configuration): output_objects.append({'object_type': 'error_text', 'text' : '''Illegal email and organization combination: Please read and follow the instructions in red on the request page! If you are a student with only a @*.ku.dk address please just use KU as organization. As long as you state that you want the certificate for course purposes in the comment field, you will be given access to the necessary resources anyway. '''}) return (output_objects, returnvalues.CLIENT_ERROR) user_dict = { 'full_name': cert_name, 'organization': org, 'state': state, 'country': country, 'email': email, 'comment': comment, 'password': base64.b64encode(password), 'expire': int(time.time() + cert_valid_days * 24 * 60 * 60), 'openid_names': [], } fill_distinguished_name(user_dict) user_id = user_dict['distinguished_name'] user_dict['authorized'] = (user_id == client_id) if configuration.user_openid_providers and configuration.user_openid_alias: user_dict['openid_names'] += \ [user_dict[configuration.user_openid_alias]] logger.info('got reqcert request: %s' % user_dict) # For testing only if cert_name.upper().find('DO NOT SEND') != -1: output_objects.append({'object_type': 'text', 'text' : "Test request ignored!"}) return (output_objects, returnvalues.OK) req_path = None try: (os_fd, req_path) = tempfile.mkstemp(dir=user_pending) os.write(os_fd, dumps(user_dict)) os.close(os_fd) except Exception, err: logger.error('Failed to write certificate request to %s: %s' % (req_path, err)) output_objects.append({'object_type': 'error_text', 'text' : 'Request could not be sent to grid administrators. Please contact them manually on %s if this error persists.' % admin_email}) return (output_objects, returnvalues.SYSTEM_ERROR)
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)
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)
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)
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 Resource' % \ 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] unique_resource_name = accepted['unique_resource_name'][-1].lower() # 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!''' % configuration.site_vgrid_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, configuration.site_vgrid_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 %(_label)s because it is a resource of a parent %(_label)s. Removal must be performed from the most significant %(_label)s possible.''' % \ {'res_name': unique_resource_name, '_label': configuration.site_vgrid_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, configuration.site_vgrid_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)
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) vgrid_name = accepted['vgrid_name'][-1].strip() title_entry = find_entry(output_objects, 'title') title_entry['text'] = 'Create %s' % configuration.site_vgrid_label output_objects.append({'object_type': 'header', 'text': 'Create %s' % \ configuration.site_vgrid_label}) # No owner check here so we need to specifically check for illegal # directory access reserved_names = (default_vgrid, any_vgrid, all_vgrids) if vgrid_name in reserved_names or \ not valid_dir_input(configuration.vgrid_home, vgrid_name): output_objects.append({'object_type': 'error_text', 'text' : 'Illegal vgrid_name: %s' % vgrid_name}) logger.warning("""createvgrid possible illegal directory access attempt by '%s': vgrid_name '%s'""" % (client_id, vgrid_name)) return (output_objects, returnvalues.CLIENT_ERROR) user_map = get_full_user_map(configuration) user_dict = user_map.get(client_id, None) # Optional limitation of create vgrid permission if not user_dict or \ not vgrid_create_allowed(configuration, user_dict): logger.warning("user %s is not allowed to create %ss!" % \ (client_id, configuration.site_vgrid_label)) output_objects.append( {'object_type': 'error_text', 'text' : 'Only privileged users can create %ss' % \ 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_home, vgrid_name)) + os.sep public_base_dir = \ os.path.abspath(os.path.join(configuration.vgrid_public_base, vgrid_name)) + os.sep public_scm_dir = \ os.path.abspath(os.path.join(configuration.vgrid_public_base, vgrid_name, '.vgridscm')) + os.sep public_tracker_dir = \ os.path.abspath(os.path.join(configuration.vgrid_public_base, vgrid_name, '.vgridtracker')) + os.sep private_base_dir = \ os.path.abspath(os.path.join(configuration.vgrid_private_base, vgrid_name)) + os.sep private_scm_dir = \ os.path.abspath(os.path.join(configuration.vgrid_private_base, vgrid_name, '.vgridscm')) + os.sep private_tracker_dir = \ os.path.abspath(os.path.join(configuration.vgrid_private_base, vgrid_name, '.vgridtracker')) + os.sep private_forum_dir = \ os.path.abspath(os.path.join(configuration.vgrid_private_base, vgrid_name, '.vgridforum')) + os.sep vgrid_files_dir = \ os.path.abspath(os.path.join(configuration.vgrid_files_home, vgrid_name)) + os.sep vgrid_scm_dir = \ os.path.abspath(os.path.join(configuration.vgrid_files_home, vgrid_name, '.vgridscm')) + os.sep vgrid_tracker_dir = \ os.path.abspath(os.path.join(configuration.vgrid_files_home, vgrid_name, '.vgridtracker')) + os.sep # does vgrid exist? if os.path.isdir(base_dir): output_objects.append( {'object_type': 'error_text', 'text' : '%s %s cannot be created because it already exists!' % (configuration.site_vgrid_label, vgrid_name)}) return (output_objects, returnvalues.CLIENT_ERROR) # verify that client is owner of imada or imada/topology if trying to # create imada/topology/test vgrid_name_list = vgrid_name.split('/') vgrid_name_list_length = len(vgrid_name_list) if vgrid_name_list_length <= 0: output_objects.append({'object_type': 'error_text', 'text' : 'vgrid_name not specified?'}) return (output_objects, returnvalues.SYSTEM_ERROR) elif vgrid_name_list_length == 1: # anyone can create base vgrid new_base_vgrid = True else: new_base_vgrid = False vgrid_name_without_last_fragment = \ '/'.join(vgrid_name_list[0:vgrid_name_list_length - 1]) parent_base = os.path.dirname(base_dir.rstrip(os.sep)) if not os.path.isdir(parent_base): output_objects.append( {'object_type': 'error_text', 'text' : 'Parent %s %s does not exist!' % \ (configuration.site_vgrid_label, vgrid_name_without_last_fragment) }) return (output_objects, returnvalues.CLIENT_ERROR) if not vgrid_is_owner(vgrid_name_without_last_fragment, client_id, configuration): output_objects.append( {'object_type': 'error_text', 'text' : 'You must own a parent %s to create a sub vgrid' % \ configuration.site_vgrid_label }) return (output_objects, returnvalues.CLIENT_ERROR) # make sure all dirs can be created (that a file or directory with the same # name do not exist prior to the vgrid creation) try_again_string = \ """%s cannot be created, a file or directory exists with the same name, please try again with a new name!""" % configuration.site_vgrid_label if os.path.exists(public_base_dir): output_objects.append({'object_type': 'error_text', 'text' : try_again_string}) return (output_objects, returnvalues.CLIENT_ERROR) if os.path.exists(private_base_dir): output_objects.append({'object_type': 'error_text', 'text' : try_again_string}) return (output_objects, returnvalues.CLIENT_ERROR) if os.path.exists(vgrid_files_dir): output_objects.append({'object_type': 'error_text', 'text' : try_again_string}) return (output_objects, returnvalues.CLIENT_ERROR) # create directory to store vgrid files try: os.mkdir(base_dir) except Exception, exc: output_objects.append( {'object_type': 'error_text', 'text' : """Could not create %(_label)s directory, remember to create parent %(_label)s before creating a sub-%(_label)s.""" % \ {'_label': configuration.site_vgrid_label} }) return (output_objects, returnvalues.CLIENT_ERROR)
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) 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] cert_id = accepted['cert_id'][-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 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) base_dir = os.path.abspath(os.path.join(configuration.resource_home, unique_resource_name)) + os.sep # Remove owner owners_file = os.path.join(base_dir, 'owners') (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', 'title': 'Administrate resource', 'text': 'Manage resource'}) return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) 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_ids = accepted['job_id'] action = accepted['action'][-1] src = accepted['src'] dst = accepted['dst'][-1] title_entry = find_entry(output_objects, 'title') title_entry['text'] = '%s live I/O' % configuration.short_title output_objects.append({'object_type': 'header', 'text' : 'Request live communication with jobs'}) 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 and 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) if not job_ids or action in interactive_actions: job_id = '' if job_ids: job_id = job_ids[-1] output_objects.append({'object_type': 'text', 'text' : ''' Fill in the live I/O details below to request communication with a running job. Job ID can be a full ID or a wild card pattern using "*" and "?" to match one or more of your job IDs. Use send output without source and destination paths to request upload of the default stdio files from the job on the resource to the associated job_output directory in your MiG home. Destination is a always handled as a directory path to put source files into. Source and destination paths are always taken relative to the job execution directory on the resource and your MiG home respectively. '''}) html = ''' <table class="liveio"> <tr> <td> <form method="post" action="liveio.py"> <table class="liveio"> <tr><td class=centertext> </td></tr> <tr><td> Action:<br /> <input type=radio name=action checked value="send" />send output <input type=radio name=action value="get" />get input </td></tr> <tr><td> Job ID:<br /> <input type=text size=60 name=job_id value="%s" /> </td></tr> <tr><td> Source path(s):<br /> <div id="srcfields"> <input type=text size=60 name=src value="" /><br /> </div> </td></tr> <tr><td> Destination path:<br /> <input type=text size=60 name=dst value="" /> </td></tr> <tr><td> <input type="submit" value="Send request" /> </td></tr> </table> </form> </td> <td> <script type="text/javascript"> fields = 1; max_fields = 64; function addInput() { if (fields < max_fields) { document.getElementById("srcfields").innerHTML += "<input type=text size=60 name=src value='' /><br />"; fields += 1; } else { alert("Maximum " + max_fields + " source fields allowed!"); document.form.add.disabled=true; } } </script> <form name="addsrcform"> <input type="button" onclick="addInput(); return false;" name="add" value="Add another source field" /> </form> </td> </tr> </table> ''' % job_id output_objects.append({'object_type': 'html_form', 'text' : html}) output_objects.append({'object_type': 'text', 'text': ''' Further live job control is avalable through your personal message queues. They provide a basic interface for centrally storing messages under your grid account and can be used to pass messages between jobs or for orchestrating jobs before and during execution. ''' }) output_objects.append({'object_type': 'link', 'destination': 'mqueue.py', 'text': 'Message queue interface'}) return (output_objects, returnvalues.OK) elif action in ['get', 'receive', 'input']: action = 'get' action_desc = 'will be downloaded to the job on the resource' elif action in ['put', 'send', 'output']: action = 'send' action_desc = 'will be uploaded from the job on the resource' else: output_objects.append({'object_type': 'error_text', 'text' : 'Invalid live io action: %s' % action}) return (output_objects, returnvalues.CLIENT_ERROR) output_objects.append({'object_type': 'text', 'text' : 'Requesting live I/O for %s' % ', '.join(job_ids)}) if action == 'get' and (not src or not dst): output_objects.append( {'object_type': 'error_text', 'text': 'src and dst parameters required for live input'}) return (output_objects, returnvalues.CLIENT_ERROR) # Automatic fall back to stdio files if output with no path provided if src: src_text = 'The files ' + ' '.join(src) else: src_text = 'The job stdio files' if dst: dst_text = 'the ' + dst + ' directory' else: dst_text = 'the corresponding job_output 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 base_dir = \ os.path.abspath(os.path.join(configuration.mrsl_files_dir, client_dir)) + os.sep filelist = [] for job_id in job_ids: job_id = job_id.strip() # is job currently being executed? # Backward compatibility - all_jobs keyword should match all jobs if job_id == all_jobs: job_id = '*' # 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 + job_id + '.mRSL') 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, job_id)) continue # Insert valid job files in filelist for later treatment 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: You do not have any matching job IDs!' % job_id}) else: filelist += match for filepath in filelist: # Extract jo_id from filepath (replace doesn't modify filepath) mrsl_file = filepath.replace(base_dir, '') job_id = mrsl_file.replace('.mRSL', '') job_dict = unpickle(filepath, logger) if not job_dict: status = returnvalues.CLIENT_ERROR output_objects.append( {'object_type': 'error_text', 'text' : ('You can only list status of your own jobs. ' 'Please verify that you submitted the mRSL file ' 'with job id "%s" (Could not unpickle mRSL file %s)' ) % (job_id, filepath)}) continue if job_dict['STATUS'] != 'EXECUTING': output_objects.append( {'object_type': 'text', 'text' : 'Job %s is not currently being executed! Job status: %s' % (job_id, job_dict['STATUS'])}) continue if job_dict['UNIQUE_RESOURCE_NAME'] == 'ARC': output_objects.append( {'object_type': 'text', 'text' : 'Job %s is submitted to ARC, details are not available!' % job_id }) continue last_live_update_dict = {} last_live_update_file = configuration.mig_system_files + os.sep\ + job_id + '.last_live_update' if os.path.isfile(last_live_update_file): last_live_update_dict_unpickled = \ unpickle(last_live_update_file, logger) if not last_live_update_dict_unpickled: output_objects.append({'object_type': 'error_text', 'text' : 'Could not unpickle %s - skipping request!' % last_live_update_file}) continue if not last_live_update_dict_unpickled.has_key( 'LAST_LIVE_UPDATE_REQUEST_TIMESTAMP'): output_objects.append( {'object_type': 'error_text', 'text': 'Could not find needed key in %s.' % last_live_update_file}) continue last_live_update_request = \ last_live_update_dict_unpickled['LAST_LIVE_UPDATE_REQUEST_TIMESTAMP' ] difference = datetime.datetime.now()- last_live_update_request try: min_delay = \ int(configuration.min_seconds_between_live_update_requests) except: min_delay = 30 if difference.seconds < min_delay: output_objects.append( {'object_type': 'error_text', 'text': ('Request not allowed, you must wait at least ' \ '%s seconds between live update requests!' ) % min_delay}) continue # save this request to file to avoid DoS from a client request loop. last_live_update_dict['LAST_LIVE_UPDATE_REQUEST_TIMESTAMP'] = \ datetime.datetime.now() pickle_ret = pickle(last_live_update_dict, last_live_update_file, logger) if not pickle_ret: output_objects.append( {'object_type': 'error_text', 'text' : 'Error saving live io request timestamp to last_live_update ' 'file, request not sent!'}) continue # # # ## job is being executed right now, send live io request to frontend # # # get resource_config, needed by scp_file_to_resource #(status, resource_config) = get_resource_configuration( # resource_home, unique_resource_name, logger) resource_config = job_dict['RESOURCE_CONFIG'] (status, exe) = get_resource_exe(resource_config, job_dict['EXE'], logger) if not status: output_objects.append( {'object_type': 'error_text', 'text' : 'Could not get exe configuration for job %s' % job_id}) continue local_file = '%s.%supdate' % (job_dict['LOCALJOBNAME'], action) if not os.path.exists(local_file): # create try: filehandle = open(local_file, 'w') filehandle.write('job_id ' + job_dict['JOB_ID'] + '\n') filehandle.write('localjobname ' + job_dict['LOCALJOBNAME'] + '\n') filehandle.write('execution_user ' + exe['execution_user'] + '\n') filehandle.write('execution_node ' + exe['execution_node'] + '\n') filehandle.write('execution_dir ' + exe['execution_dir'] + '\n') filehandle.write('target liveio\n') # Leave defaults src and dst to FE script if not provided if src: filehandle.write('source ' + ' '.join(src) + '\n') if dst: filehandle.write('destination ' + dst + '\n') # Backward compatible test for shared_fs - fall back to scp if exe.has_key('shared_fs') and exe['shared_fs']: filehandle.write('copy_command cp\n') filehandle.write('copy_frontend_prefix \n') filehandle.write('copy_execution_prefix \n') else: filehandle.write('copy_command scp -B\n') filehandle.write('copy_frontend_prefix ${frontend_user}@${frontend_node}:\n' ) filehandle.write('copy_execution_prefix ${execution_user}@${execution_node}:\n' ) filehandle.write('### END OF SCRIPT ###\n') filehandle.close() except Exception, exc: pass if not os.path.exists(local_file): output_objects.append( {'object_type': 'error_text', 'text' : '.%supdate file not available on %s server' % \ (action, configuration.short_title)}) continue scpstatus = copy_file_to_resource(local_file, '%s.%supdate' % (job_dict['LOCALJOBNAME'], action), resource_config, logger) if not scpstatus: output_objects.append( {'object_type': 'error_text', 'text' : 'Error sending request for live io to resource!'}) continue else: output_objects.append( {'object_type': 'text', 'text' : 'Request for live io was successfully sent to the resource!' }) output_objects.append( {'object_type': 'text', 'text' : '%s %s and should become available in %s in a minute.' % \ (src_text, action_desc, dst_text) }) if action == 'send': if not dst: target_path = '%s/%s/*' % (job_output_dir, job_id) else: target_path = dst output_objects.append({'object_type': 'link', 'destination' : 'ls.py?path=%s' % target_path, 'text': 'View uploaded files'}) else: output_objects.append({'object_type': 'link', 'destination' : 'ls.py?path=%s' % ';path='.join(src), 'text': 'View files for download'}) try: os.remove(local_file) except Exception, exc: pass
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) 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 title_entry = find_entry(output_objects, 'title') title_entry['text'] = 'Remove %s' % configuration.site_vgrid_label output_objects.append({'object_type': 'header', 'text' : 'Remove %s Owner' % \ configuration.site_vgrid_label}) # 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): output_objects.append({'object_type': 'error_text', 'text' : '%s is not an owner of %s or a parent %s.' % (cert_id, vgrid_name, configuration.site_vgrid_label)}) return (output_objects, returnvalues.CLIENT_ERROR) # we need the local owners file to detect inherited ownerships (status, owners_direct) = vgrid_owners(vgrid_name, configuration, False) (all_status, owners) = vgrid_owners(vgrid_name, configuration, True) if not 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) # 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.debug('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, configuration.site_vgrid_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, configuration.site_vgrid_label, inherit_vgrid_member, configuration.site_vgrid_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, configuration.site_vgrid_label)}) js_name = 'rmvgridowner%s' % hexlify(vgrid_name) helper = html_post_helper(js_name, 'rmvgridowner.py', {'vgrid_name': vgrid_name, 'cert_id': cert_id, 'flags': 'f'}) output_objects.append({'object_type': 'html_form', 'text': helper}) output_objects.append({'object_type': 'link', 'destination': "javascript: %s();" % js_name, 'class': 'removelink', '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 (status, subs) = vgrid_list_subvgrids(vgrid_name, configuration) if not status: logger.error('Error loading sub-%ss for %s: %s)' % (configuration.site_vgrid_label, 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-%ss %s.' % (configuration.site_vgrid_label, subs)) output_objects.append({'object_type': 'error_text', 'text' : \ '%s has sub-structures and cannot be deleted.' % vgrid_name}) output_objects.append({'object_type': 'text', 'text' : ''' To leave (and delete) %s, first remove its sub-structures: %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.' % \ (configuration.site_vgrid_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) # 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 if (cert_id in owners_direct): # owner owns this vgrid, direct ownership logger.debug('%s looks like a top-level %s.' % \ (configuration.site_vgrid_label, 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, msg1) = unlink_share(user_dir, vgrid_name) (web_lnk, msg1) = unlink_web_folders(user_dir, vgrid_name) (abandoned, msg2) = abandon_vgrid_files(vgrid_name, configuration) else: # owner owns an upper vgrid, ownership is inherited logger.debug('%s looks like a sub-%s, ownership inherited.' % (vgrid_name, configuration.site_vgrid_label)) logger.debug('Only removing entry, leaving files in place.') share_lnk = True web_lnk = True abandoned = True msg1 = '' msg2 = '' (removed, msg3) = 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': 'vgridadmin.py', 'text': 'Back to the overview.'}) if not share_lnk or not web_lnk or not abandoned or not removed: logger.error('Errors while removing %s:\n%s.' % (vgrid_name, '\n'.join([msg1,msg2,msg3]))) 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)
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 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) 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].strip() cert_id = accepted['cert_id'][-1].strip() if not is_owner(client_id, unique_resource_name, configuration.resource_home, logger): output_objects.append({'object_type': 'error_text', 'text' : 'You must be an owner of %s to add a new owner!' % unique_resource_name}) return (output_objects, returnvalues.CLIENT_ERROR) # is_owner incorporates unique_resource_name verification - no need to # specifically check for illegal directory traversal 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) # don't add if already an owner if resource_is_owner(unique_resource_name, cert_id, configuration): output_objects.append({'object_type': 'error_text', 'text' : '%s is already an owner of %s.' % (cert_id, unique_resource_name)}) return (output_objects, returnvalues.CLIENT_ERROR) # Please note that base_dir must end in slash to avoid access to other # resource dirs when own name is a prefix of another user name base_dir = \ os.path.abspath(os.path.join(configuration.resource_home, unique_resource_name)) + os.sep # Add owner (add_status, add_msg) = resource_add_owners(configuration, unique_resource_name, [cert_id]) if not add_status: output_objects.append({'object_type': 'error_text', 'text' : 'Could not add new owner, reason: %s' % add_msg}) return (output_objects, returnvalues.SYSTEM_ERROR) output_objects.append({'object_type': 'text', 'text' : 'New owner %s successfully added to %s!' % (cert_id, unique_resource_name)}) output_objects.append({'object_type': 'html_form', 'text' : """ <form method='post' action='sendrequestaction.py'> <input type=hidden name=request_type value='resourceaccept' /> <input type=hidden name=unique_resource_name value='%s' /> <input type=hidden name=cert_id value='%s' /> <input type=hidden name=protocol value='%s' /> <table> <tr> <td class='title'>Custom message to user</td> </tr> <tr> <td><textarea name=request_text cols=72 rows=10> We have granted you ownership access to our %s resource. You can access the resource administration page from the Resources page. Regards, the %s resource owners </textarea></td> </tr> <tr> <td><input type='submit' value='Inform user' /></td> </tr> </table> </form> <br /> """ % (unique_resource_name, cert_id, any_protocol, unique_resource_name, unique_resource_name)}) output_objects.append({'object_type': 'link', 'destination': 'resadmin.py?unique_resource_name=%s' % \ unique_resource_name, 'class': 'adminlink', 'title': 'Administrate resource', 'text': 'Manage resource'}) return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = initialize_main_variables(client_id, op_header=False) client_dir = client_id_dir(client_id) status = returnvalues.OK defaults = signature()[1] # IMPORTANT: the CGI front end forces the input extraction to be delayed # We must manually extract and parse input here to avoid memory explosion # for huge files! # TODO: explosions still happen sometimes! # Most likely because of Apache SSL renegotiations which have # no other way of storing input extract_input = user_arguments_dict["__DELAYED_INPUT__"] logger.info("Extracting input in %s" % op_name) form = extract_input() logger.info("After extracting input in %s" % op_name) file_item = None file_name = "" user_arguments_dict = {} if form.has_key("fileupload"): file_item = form["fileupload"] file_name = file_item.filename user_arguments_dict["fileupload"] = ["true"] user_arguments_dict["path"] = [file_name] if form.has_key("path"): user_arguments_dict["path"] = [form["path"].value] if form.has_key("restrict"): user_arguments_dict["restrict"] = [form["restrict"].value] else: user_arguments_dict["restrict"] = defaults["restrict"] logger.info("Filtered input is: %s" % user_arguments_dict) # Now validate parts as usual (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"]) path = accepted["path"][-1] restrict = accepted["restrict"][-1] 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) logger.info("Filtered input validated with result: %s" % accepted) # 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)}) output_objects.append({"object_type": "header", "text": "Uploading file"}) # Check directory traversal attempts before actual handling to avoid # leaking information about file system layout while allowing consistent # error messages real_path = os.path.realpath(os.path.join(base_dir, path)) # Implicit destination if os.path.isdir(real_path): real_path = os.path.join(real_path, os.path.basename(file_name)) if not valid_user_path(real_path, base_dir, True): logger.warning("%s tried to %s restricted path %s ! (%s)" % (client_id, op_name, real_path, path)) output_objects.append( {"object_type": "error_text", "text": "Invalid destination (%s expands to an illegal path)" % path} ) return (output_objects, returnvalues.CLIENT_ERROR) if not os.path.isdir(os.path.dirname(real_path)): output_objects.append( {"object_type": "error_text", "text": "cannot write: no such file or directory: %s)" % path} ) return (output_objects, returnvalues.CLIENT_ERROR) # We fork off here and redirect the user to a progress page for user # friendly output and to avoid cgi timeouts from killing the upload. # We use something like the Active State python recipe for daemonizing # to properly detach from the CGI process and continue in the background. # Please note that we only close stdio file descriptors to avoid closing # the fileupload. file_item.file.seek(0, 2) total_size = file_item.file.tell() file_item.file.seek(0, 0) try: pid = os.fork() if pid == 0: os.setsid() pid = os.fork() if pid == 0: os.chdir("/") os.umask(0) for fno in range(3): try: os.close(fno) except OSError: pass else: os._exit(0) except OSError, ose: output_objects.append( { "object_type": "error_text", "text": "%s upload could not background! (%s)" % (path, str(ose).replace(base_dir, "")), } ) return (output_objects, returnvalues.SYSTEM_ERROR)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) title_entry = find_entry(output_objects, 'title') title_entry['text'] = 'Delete frozen archive' defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) if not correct_handler('POST'): output_objects.append( {'object_type': 'error_text', 'text' : 'Only accepting POST requests to prevent unintended updates'}) return (output_objects, returnvalues.CLIENT_ERROR) flavor = accepted['flavor'][-1] if not flavor in freeze_flavors.keys(): output_objects.append({'object_type': 'error_text', 'text': 'Invalid freeze flavor: %s' % flavor}) return (output_objects, returnvalues.CLIENT_ERROR) title = freeze_flavors[flavor]['deletefreeze_title'] output_objects.append({'object_type': 'header', 'text': title}) title_entry = find_entry(output_objects, 'title') title_entry['text'] = title if not configuration.site_enable_freeze: output_objects.append({'object_type': 'text', 'text': '''Freezing archives is disabled on this site. Please contact the Grid admins %s if you think it should be enabled. ''' % configuration.admin_email}) return (output_objects, returnvalues.OK) freeze_id = accepted['freeze_id'][-1] # NB: the restrictions on freeze_id prevents illegal directory traversal if not is_frozen_archive(freeze_id, configuration): logger.error("%s: invalid freeze '%s': %s" % (op_name, client_id, freeze_id)) output_objects.append({'object_type': 'error_text', 'text': "No such frozen archive: '%s'" % freeze_id}) return (output_objects, returnvalues.CLIENT_ERROR) (load_status, freeze_dict) = get_frozen_archive(freeze_id, configuration) if not load_status: logger.error("%s: load failed for '%s': %s" % \ (op_name, freeze_id, freeze_dict)) output_objects.append( {'object_type': 'error_text', 'text': 'Could not read frozen archive details for %s' % freeze_id}) return (output_objects, returnvalues.SYSTEM_ERROR) # Prevent easy delete if the frozen archive if configuration forbids it if configuration.site_permanent_freeze: output_objects.append( {'object_type': 'error_text', 'text': "Can't delete frozen archive '%s' yourself due to site policy" % freeze_id}) return (output_objects, returnvalues.CLIENT_ERROR) # Make sure the frozen archive belongs to the user trying to delete it if client_id != freeze_dict['CREATOR']: logger.error("%s: illegal access attempt for '%s': %s" % \ (op_name, freeze_id, client_id)) output_objects.append({'object_type': 'error_text', 'text': \ 'You are not the owner of frozen archive "%s"' % freeze_id}) return (output_objects, returnvalues.CLIENT_ERROR) if freeze_dict.get('FLAVOR','freeze') != flavor: logger.error("%s: flavor mismatch for '%s': %s vs %s" % \ (op_name, freeze_id, flavor, freeze_dict)) output_objects.append({'object_type': 'error_text', 'text' : 'No such %s archive "%s"' % (flavor, freeze_id)}) return (output_objects, returnvalues.CLIENT_ERROR) # Delete the frozen archive (status, msg) = delete_frozen_archive(freeze_id, configuration) # If something goes wrong when trying to delete frozen archive # freeze_id, an error is displayed. if not status: logger.error("%s: failed for '%s': %s" % (op_name, freeze_id, msg)) output_objects.append({'object_type': 'error_text', 'text' : 'Could not remove %s frozen archive: %s' % (freeze_id, msg)}) return (output_objects, returnvalues.SYSTEM_ERROR) # If deletion of frozen archive freeze_id is successful, we just # return OK else: logger.info("%s: successful for '%s': %s" % (op_name, freeze_id, client_id)) output_objects.append( {'object_type': 'text', 'text' : 'Successfully deleted frozen archive: "%s"' % freeze_id}) output_objects.append({'object_type': 'link', 'destination': 'freezedb.py', 'class': 'infolink', 'title': 'Show frozen archives', 'text': 'Show frozen archives'}) return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False, op_menu=client_id) output_objects.append({'object_type': 'header', 'text' : '%s Screen Saver Sandbox Download' % \ configuration.short_title }) defaults = signature()[1] (validate_status, accepted) = validate_input(user_arguments_dict, defaults, output_objects, 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) username = accepted['username'][-1] password = accepted['password'][-1] hd_size = accepted['hd_size'][-1] image_format = accepted['image_format'][-1] net_bw = accepted['net_bw'][-1] memory = accepted['memory'][-1] operating_system = accepted['operating_system'][-1] win_solution = accepted['win_solution'][-1] vgrid_list = accepted['vgrid'] cputime = 1000000 sandboxkey = hexlify(open('/dev/urandom').read(32)) ip_address = 'UNKNOWN' if os.environ.has_key('REMOTE_ADDR'): ip_address = os.environ['REMOTE_ADDR'] if not configuration.site_enable_sandboxes: output_objects.append({'object_type': 'text', 'text': '''Sandbox resources 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) # check that requested image format is valid if not image_format in ['raw', 'qcow', 'cow', 'qcow2', 'vmdk']: output_objects.append({'object_type': 'error_text', 'text' : 'Unsupported image format: %s' % image_format}) return (output_objects, returnvalues.CLIENT_ERROR) # check that requested vgrids are valid - anybody can offer their sandbox # for a vgrid but it is still left to the vgrid owners to explicitly # accept all resources (vg_status, all_vgrids) = vgrid_list_vgrids(configuration) for vgrid in vgrid_list: if not vg_status or not vgrid in all_vgrids: output_objects.append({'object_type': 'error_text', 'text' : 'Failed to validate %s %s: %s' % (configuration.site_vgrid_label, vgrid, all_vgrids)}) return (output_objects, returnvalues.SYSTEM_ERROR) # Load the user file try: userdb = load_sandbox_db(configuration) except Exception, exc: output_objects.append({'object_type': 'error_text', 'text' : 'Failed to read login info: %s' % exc}) return (output_objects, returnvalues.SYSTEM_ERROR)
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']) src_list = accepted['src'] dst = accepted['dst'][-1] # Please note that base_dir must end in slash to avoid access to other # user dirs when own name is a prefix of another user name base_dir = os.path.abspath(os.path.join(configuration.user_home, client_dir)) + os.sep status = returnvalues.OK real_dest = base_dir + dst dst_list = glob.glob(real_dest) if not dst_list: # New destination? if not glob.glob(os.path.dirname(real_dest)): output_objects.append({'object_type': 'error_text', 'text' : 'Illegal dst path provided!'}) return (output_objects, returnvalues.CLIENT_ERROR) else: dst_list = [real_dest] # Use last match in case of multiple matches dest = dst_list[-1] if len(dst_list) > 1: output_objects.append( {'object_type': 'warning', 'text' : 'dst (%s) matches multiple targets - using last: %s' % (dst, dest)}) real_dest = os.path.abspath(dest) # Don't use real_path in output as it may expose underlying # fs layout. relative_dest = real_dest.replace(base_dir, '') if not valid_user_path(real_dest, base_dir, True): logger.warning('%s tried to %s to restricted path %s ! (%s)' % (client_id, op_name, real_dest, dst)) output_objects.append( {'object_type': 'error_text', 'text' : "Invalid path! (%s expands to an illegal path)" % dst}) return (output_objects, returnvalues.CLIENT_ERROR) for pattern in src_list: 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): 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: no such file or directory! %s' % (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 os.path.islink(real_path): output_objects.append( {'object_type': 'warning', 'text' : "You're not allowed to move entire %s shared dirs!" % configuration.site_vgrid_label}) status = returnvalues.CLIENT_ERROR continue # If destination is a directory the src should be moved in there # Move with existing directory as target replaces the directory! real_target = real_dest if os.path.isdir(real_target): if os.path.samefile(real_target, real_path): output_objects.append( {'object_type': 'warning', 'text' : "Cannot move '%s' to a subdirectory of itself!" % \ relative_path }) status = returnvalues.CLIENT_ERROR continue real_target = os.path.join(real_target, os.path.basename(real_path)) try: shutil.move(real_path, real_target) 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
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False, op_menu=client_id) defaults = signature()[1] (validate_status, accepted) = validate_input(user_arguments_dict, defaults, output_objects, allow_rejects=False) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) queue = accepted['queue'][-1] action = accepted['action'][-1] iosessionid = accepted['iosessionid'][-1] msg = accepted['msg'][-1] msg_id = accepted['msg_id'][-1] # Web format for cert access and no header for SID access if client_id: output_objects.append({'object_type': 'header', 'text' : 'Message queue %s' % action}) else: output_objects.append({'object_type': 'start'}) # Always return at least a basic file_output entry file_entry = {'object_type': 'file_output', 'lines': [], 'wrap_binary': True, 'wrap_targets': ['lines']} if not action in valid_actions: output_objects.append({'object_type': 'error_text', 'text' : 'Invalid action "%s" (supported: %s)' % \ (action, ', '.join(valid_actions))}) output_objects.append(file_entry) return (output_objects, returnvalues.CLIENT_ERROR) if action in post_actions and 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) # Find user home from session or certificate if iosessionid: client_home = os.path.realpath(os.path.join(configuration.webserver_home, iosessionid)) client_dir = os.path.basename(client_home) elif client_id: client_dir = client_id_dir(client_id) else: output_objects.append({'object_type': 'error_text', 'text' : 'Either certificate or session ID is required' }) output_objects.append(file_entry) 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 not os.path.isdir(base_dir): output_objects.append({'object_type': 'error_text', 'text' : 'No matching session or user home!'}) output_objects.append(file_entry) return (output_objects, returnvalues.CLIENT_ERROR) mqueue_base = os.path.join(base_dir, mqueue_prefix) + os.sep default_queue_dir = os.path.join(mqueue_base, default_mqueue) # Create mqueue base and default queue dir if missing if not os.path.exists(default_queue_dir): try: os.makedirs(default_queue_dir) except: pass queue_path = os.path.abspath(os.path.join(mqueue_base, queue)) if not valid_user_path(queue_path, mqueue_base): output_objects.append({'object_type': 'error_text', 'text' : 'Invalid queue name: "%s"' % queue}) output_objects.append(file_entry) return (output_objects, returnvalues.CLIENT_ERROR) lock_path = os.path.join(mqueue_base, lock_name) lock_handle = open(lock_path, 'a') fcntl.flock(lock_handle.fileno(), fcntl.LOCK_EX) status = returnvalues.OK if action == "interactive": output_objects.append({'object_type': 'text', 'text' : ''' Fill in the fields below to control and access your personal message queues. Jobs can receive from and send to the message queues during execution, and use them as a means of job inter-communication. Expect message queue operations to take several seconds on the resources, however. That is, use it for tasks like orchestrating long running jobs, and not for low latency communication. '''}) html = ''' <form name="mqueueform" method="post" action="mqueue.py"> <table class="mqueue"> <tr><td class=centertext> </td></tr> <tr><td> Action:<br /> <input type=radio name=action value="create" onclick="javascript: document.mqueueform.queue.disabled=false; document.mqueueform.msg.disabled=true;" />create queue <input type=radio name=action checked value="send" onclick="javascript: document.mqueueform.queue.disabled=false; document.mqueueform.msg.disabled=false;" />send message to queue <input type=radio name=action value="receive" onclick="javascript: document.mqueueform.queue.disabled=false; document.mqueueform.msg.disabled=true;" />receive message from queue <input type=radio name=action value="remove" onclick="javascript: document.mqueueform.queue.disabled=false; document.mqueueform.msg.disabled=true;" />remove queue <input type=radio name=action value="listqueues" onclick="javascript: document.mqueueform.queue.disabled=true; document.mqueueform.msg.disabled=true;" />list queues <input type=radio name=action value="listmessages" onclick="javascript: document.mqueueform.queue.disabled=false; document.mqueueform.msg.disabled=true;" />list messages <input type=radio name=action value="show" onclick="javascript: document.mqueueform.queue.disabled=false; document.mqueueform.msg.disabled=true;" />show message </td></tr> <tr><td> Queue:<br /> <input type=text size=60 name=queue value="%s" /> </td></tr> <tr><td> <div id="msgfieldf"> <input type=text size=60 name=msg value="%s" /><br /> </div> </td></tr> <tr><td> <input type="submit" value="Apply" /> </td></tr> </table> </form> ''' % (queue, msg) output_objects.append({'object_type': 'html_form', 'text' : html}) output_objects.append({'object_type': 'text', 'text': ''' Further live job control is avalable through the live I/O interface. They provide a basic interface for centrally managing input and output files for active jobs. ''' }) output_objects.append({'object_type': 'link', 'destination': 'liveio.py', 'text': 'Live I/O interface'}) return (output_objects, returnvalues.OK) elif action == 'create': try: os.mkdir(queue_path) output_objects.append({'object_type': 'text', 'text': 'New "%s" queue created' % queue}) except Exception, err: output_objects.append({'object_type': 'error_text', 'text' : 'Could not create "%s" queue: "%s"' % \ (queue, err)}) status = returnvalues.CLIENT_ERROR
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)
def main(client_id, user_arguments_dict): """ Main function used by front end. :param client_id: A MiG user. :param user_arguments_dict: A JSON message sent to the MiG. This will be parsed and if valid, the relevant API handler functions are called to generate meaningful output. :return: (Tuple (list, Tuple(integer,string))) Returns a tuple with the first value being a list of output objects generated by the call. The second value is also a tuple used for error code reporting, with the first value being an error code and the second being a brief explanation. """ # Ensure that the output format is in JSON user_arguments_dict['output_format'] = ['json'] user_arguments_dict.pop('__DELAYED_INPUT__', None) (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_title=False, op_header=False, op_menu=False) # Add allow Access-Control-Allow-Origin to headers # Required to allow Jupyter Widget from localhost to request against the # API # TODO, possibly restrict allowed origins output_objects[0]['headers'].append(('Access-Control-Allow-Origin', '*')) output_objects[0]['headers'].append( ('Access-Control-Allow-Headers', 'Content-Type')) output_objects[0]['headers'].append(('Access-Control-Max-Age', 600)) output_objects[0]['headers'].append( ('Access-Control-Allow-Methods', 'POST, OPTIONS')) output_objects[0]['headers'].append(('Content-Type', 'application/json')) if not correct_handler('POST'): msg = "Interaction from %s not POST request" % client_id logger.error(msg) output_objects.append({'object_type': 'error_text', 'text': msg}) return (output_objects, returnvalues.SYSTEM_ERROR) if not configuration.site_enable_workflows: output_objects.append({ 'object_type': 'error_text', 'text': 'Workflows are not enabled on this system' }) return (output_objects, returnvalues.SYSTEM_ERROR) # Input data data = sys.stdin.read() try: json_data = json.loads(data, object_hook=force_utf8_rec) except ValueError: msg = "An invalid format was supplied to: '%s', requires a JSON " \ "compatible format" % op_name logger.error(msg) output_objects.append({'object_type': 'error_text', 'text': msg}) return (output_objects, returnvalues.CLIENT_ERROR) # TODO: consider additional CSRF protection here? # attacker needs to intercept jupyter session_id from running session # and work around security restrictions in Jupyter API to abuse anything # https://github.com/jupyter/jupyter/wiki/Jupyter-Notebook-Server-API # IMPORTANT!! Do not access the json_data input before it has been # validated by validated_input. accepted, rejected = validated_input(json_data, WORKFLOW_SIGNATURE, type_override=WORKFLOWS_TYPE_MAP, value_override=WORKFLOW_VALUE_MAP, list_wrap=True) if not accepted or rejected: logger.error("A validation error occurred: '%s'" % rejected) msg = "Invalid input was supplied to the workflow API: %s" % rejected # TODO, Transform error messages to something more readable output_objects.append({'object_type': 'error_text', 'text': msg}) return (output_objects, returnvalues.CLIENT_ERROR) # Chould use 'accepted' here, but all data jumbled together into one big # dict, easier to access json data by known keys workflow_attributes = json_data.get('attributes', None) workflow_type = json_data.get('type', None) operation = json_data.get('operation', None) workflow_session_id = json_data.get('workflowsessionid', None) if not valid_session_id(configuration, workflow_session_id): output_objects.append({ 'object_type': 'error_text', 'text': 'Invalid workflowsessionid' }) return (output_objects, returnvalues.CLIENT_ERROR) # workflow_session_id symlink points to the vGrid it gives access to workflow_sessions_db = [] try: workflow_sessions_db = load_workflow_sessions_db(configuration) except IOError: logger.debug("Workflow sessions db didn't load, creating new db") if not touch_workflow_sessions_db(configuration, force=True): output_objects.append({ 'object_type': 'error_text', 'text': "Internal sessions db failure, please contact " "an admin at '%s' to resolve this issue." % configuration.admin_email }) return (output_objects, returnvalues.SYSTEM_ERROR) else: # Try reload workflow_sessions_db = load_workflow_sessions_db(configuration) if workflow_session_id not in workflow_sessions_db: logger.error("Workflow session '%s' from user '%s' not found in " "database" % (workflow_session_id, client_id)) configuration.auth_logger.error( "Workflow session '%s' provided by user '%s' but not present in " "database" % (workflow_session_id, client_id)) # TODO Also track multiple attempts from the same IP output_objects.append({ 'object_type': 'error_text', 'text': 'Invalid workflowsessionid' }) return (output_objects, returnvalues.CLIENT_ERROR) workflow_session = workflow_sessions_db.get(workflow_session_id) logger.info('workflowjsoninterface found %s' % workflow_session) # Create if operation == WORKFLOW_API_CREATE: created, msg = workflow_api_create(configuration, workflow_session, workflow_type, **workflow_attributes) if not created: output_objects.append({'object_type': 'error_text', 'text': msg}) logger.error("Returning error msg '%s'" % msg) return (output_objects, returnvalues.CLIENT_ERROR) output_objects.append({'object_type': 'workflows', 'text': msg}) return (output_objects, returnvalues.OK) # Read if operation == WORKFLOW_API_READ: workflows, msg = workflow_api_read(configuration, workflow_session, workflow_type, **workflow_attributes) if not workflows: output_objects.append({'object_type': 'error_text', 'text': msg}) return (output_objects, returnvalues.OK) output_objects.append({ 'object_type': 'workflows', 'workflows': workflows }) return (output_objects, returnvalues.OK) # Update if operation == WORKFLOW_API_UPDATE: updated, msg = workflow_api_update(configuration, workflow_session, workflow_type, **workflow_attributes) if not updated: output_objects.append({'object_type': 'error_text', 'text': msg}) return (output_objects, returnvalues.OK) output_objects.append({'object_type': 'workflows', 'text': msg}) return (output_objects, returnvalues.OK) # Delete if operation == WORKFLOW_API_DELETE: deleted, msg = workflow_api_delete(configuration, workflow_session, workflow_type, **workflow_attributes) if not deleted: output_objects.append({'object_type': 'error_text', 'text': msg}) return (output_objects, returnvalues.OK) output_objects.append({'object_type': 'workflows', 'text': msg}) return (output_objects, returnvalues.OK) output_objects.append({ 'object_type': 'error_text', 'text': 'You are out of bounds here' }) return (output_objects, returnvalues.CLIENT_ERROR)
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)})
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 STOP 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) 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] 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 stop 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() for (store_name, task_list) in workers: (status, msg) = task_list[0].finish() output_objects.append({'object_type': 'header', 'text' : 'Stop store'}) if not status: output_objects.append({'object_type': 'error_text', 'text' : 'Problems stopping store: %s' % msg}) exit_status = returnvalues.SYSTEM_ERROR else: output_objects.append({'object_type': 'text', 'text' : 'Stop store success: %s' % msg}) return (output_objects, exit_status)
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)
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) path = accepted['path'][-1] chosen_newline = accepted['newline'][-1] submitjob = accepted['submitjob'][-1] # Please note that base_dir must end in slash to avoid access to other # user dirs when own name is a prefix of another user name base_dir = os.path.abspath(os.path.join(configuration.user_home, client_dir)) + os.sep # HTML spec dictates newlines in forms to be MS style (\r\n) # rather than un*x style (\n): change if requested. form_newline = '\r\n' allowed_newline = {'unix': '\n', 'mac': '\r', 'windows': '\r\n'} output_objects.append({'object_type': 'header', 'text' : 'Saving changes to edited file'}) if not chosen_newline in allowed_newline.keys(): output_objects.append( {'object_type': 'error_text', 'text' : 'Unsupported newline style supplied: %s (must be one of %s)' % (chosen_newline, ', '.join(allowed_newline.keys()))}) return (output_objects, returnvalues.CLIENT_ERROR) saved_newline = allowed_newline[chosen_newline] # Check directory traversal attempts before actual handling to avoid # leaking information about file system layout while allowing consistent # error messages real_path = '' unfiltered_match = glob.glob(base_dir + path) for server_path in unfiltered_match: real_path = os.path.abspath(server_path) if not valid_user_path(real_path, base_dir, True): logger.warning('%s tried to %s restricted path %s ! (%s)' % (client_id, op_name, real_path, path)) output_objects.append( {'object_type': 'error_text', 'text' : "Invalid path! (%s expands to an illegal path)" % path}) return (output_objects, returnvalues.CLIENT_ERROR) if real_path == '': real_path = base_dir + path if not valid_user_path(real_path, base_dir, True): logger.warning('%s tried to %s restricted path %s ! (%s)' % (client_id, op_name, real_path, path)) output_objects.append( {'object_type': 'error_text', 'text' : "Invalid path! (%s expands to an illegal path)" % path}) return (output_objects, returnvalues.CLIENT_ERROR) (owner, time_left) = acquire_edit_lock(real_path, client_id) if owner != client_id: output_objects.append({'object_type': 'error_text', 'text' : "You don't have the lock for %s!" % path}) return (output_objects, returnvalues.CLIENT_ERROR) try: fh = open(real_path, 'w+') fh.write(user_arguments_dict['editarea' ][0].replace(form_newline, saved_newline)) fh.close() # everything ok output_objects.append({'object_type': 'text', 'text' : 'Saved changes to %s.' % path}) release_edit_lock(real_path, client_id) except Exception, exc: # Don't give away information about actual fs layout output_objects.append({'object_type': 'error_text', 'text' : '%s could not be written! (%s)' % (path, str(exc).replace(base_dir, '' ))}) return (output_objects, returnvalues.SYSTEM_ERROR)
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
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 settings' % configuration.short_title }) defaults = signature()[1] extend_defaults(defaults, user_arguments_dict) (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) topic = accepted['topic'][-1] topic_mrsl = '' if topic == 'general': keywords_dict = settings_keywords() elif topic == 'widgets': keywords_dict = widgets_keywords() elif topic == 'profile': keywords_dict = profile_keywords() elif topic in ('sftp', 'webdavs', 'ftps'): # We don't use mRSL parser here keywords_dict = {} else: # should never get here keywords_dict = {} for keyword in keywords_dict.keys(): received_arguments = accepted[keyword] if received_arguments != None and received_arguments != ['\r\n' ]: topic_mrsl += '''::%s:: %s ''' % (keyword.upper(), '\n'.join(received_arguments)) # Save content to temp file try: (filehandle, tmptopicfile) = tempfile.mkstemp(text=True) os.write(filehandle, topic_mrsl) os.close(filehandle) except Exception: output_objects.append( {'object_type': 'error_text', 'text': 'Problem writing temporary topic file on server.'}) return (output_objects, returnvalues.SYSTEM_ERROR) # Parse topic if topic == 'general': (parse_status, parse_msg) = \ parse_and_save_settings(tmptopicfile, client_id, configuration) elif topic == 'widgets': (parse_status, parse_msg) = \ parse_and_save_widgets(tmptopicfile, client_id, configuration) elif topic == 'profile': (parse_status, parse_msg) = \ parse_and_save_profile(tmptopicfile, client_id, configuration) elif topic == 'sftp': publickeys = '\n'.join(accepted.get('publickeys', [''])) password = accepted.get('password', [''])[-1].strip() (parse_status, parse_msg) = \ parse_and_save_ssh(publickeys, password, client_id, configuration) elif topic == 'webdavs': publickeys = '\n'.join(accepted.get('publickeys', [''])) password = accepted.get('password', [''])[-1].strip() (parse_status, parse_msg) = \ parse_and_save_davs(publickeys, password, client_id, configuration) elif topic == 'ftps': publickeys = '\n'.join(accepted.get('publickeys', [''])) password = accepted.get('password', [''])[-1].strip() (parse_status, parse_msg) = \ parse_and_save_ftps(publickeys, password, client_id, configuration) else: output_objects.append({'object_type': 'error_text', 'text' : 'No such settings topic: %s' % topic }) return (output_objects, returnvalues.CLIENT_ERROR) try: os.remove(tmptopicfile) except Exception, exc: pass # probably deleted by parser!
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
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, op_header=False, op_menu=False) logger = configuration.logger logger.info('%s: args: %s' % (op_name, user_arguments_dict)) prefilter_map = {} output_objects.append({'object_type': 'header', 'text' : 'Automatic %s sign up' % \ configuration.short_title }) identity = extract_client_openid(configuration, environ, lookup_dn=False) if client_id and client_id == identity: login_type = 'cert' base_url = configuration.migserver_https_cert_url elif identity: login_type = 'oid' base_url = configuration.migserver_https_oid_url for name in ('openid.sreg.cn', 'openid.sreg.fullname', 'openid.sreg.full_name'): prefilter_map[name] = filter_commonname else: output_objects.append( {'object_type': 'error_text', 'text': 'Missing user credentials'}) return (output_objects, returnvalues.CLIENT_ERROR) defaults = signature(login_type)[1] (validate_status, accepted) = validate_input( user_arguments_dict, defaults, output_objects, allow_rejects=False, prefilter_map=prefilter_map) if not validate_status: logger.warning('%s invalid input: %s' % (op_name, accepted)) return (accepted, returnvalues.CLIENT_ERROR) logger.debug('Accepted arguments: %s' % accepted) # Unfortunately OpenID redirect does not use POST if login_type != 'oid' and 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) admin_email = configuration.admin_email openid_names, oid_extras = [], {} # Extract raw values if login_type == 'cert': uniq_id = accepted['cert_id'][-1].strip() raw_name = accepted['cert_name'][-1].strip() country = accepted['country'][-1].strip() state = accepted['state'][-1].strip() org = accepted['org'][-1].strip() org_unit = '' role = ','.join([i for i in accepted['role'] if i]) locality = '' timezone = '' email = accepted['email'][-1].strip() raw_login = None elif login_type == 'oid': uniq_id = accepted['openid.sreg.nickname'][-1].strip() or \ accepted['openid.sreg.short_id'][-1].strip() raw_name = accepted['openid.sreg.fullname'][-1].strip() or \ accepted['openid.sreg.full_name'][-1].strip() country = accepted['openid.sreg.country'][-1].strip() state = accepted['openid.sreg.state'][-1].strip() org = accepted['openid.sreg.o'][-1].strip() or \ accepted['openid.sreg.organization'][-1].strip() org_unit = accepted['openid.sreg.ou'][-1].strip() or \ accepted['openid.sreg.organizational_unit'][-1].strip() # We may receive multiple roles role = ','.join([i for i in accepted['openid.sreg.role'] if i]) locality = accepted['openid.sreg.locality'][-1].strip() timezone = accepted['openid.sreg.timezone'][-1].strip() email = accepted['openid.sreg.email'][-1].strip() # Fix case of values: # force name to capitalized form (henrik karlsen -> Henrik Karlsen) # please note that we get utf8 coded bytes here and title() treats such # chars as word termination. Temporarily force to unicode. try: full_name = force_utf8(force_unicode(raw_name).title()) except Exception: logger.warning("could not use unicode form to capitalize full name") full_name = raw_name.title() country = country.upper() state = state.upper() email = email.lower() if login_type == 'oid': # Remap some oid attributes if on kit format with faculty in # organization and institute in organizational_unit. We can add them # as different fields as long as we make sure the x509 fields are # preserved. # We do that to allow autocreate updating existing cert users. if org_unit not in ('', 'NA'): org_unit = org_unit.upper() oid_extras['faculty'] = org oid_extras['institute'] = org_unit org = org_unit.upper() org_unit = 'NA' # Stay on virtual host - extra useful while we test dual OpenID base_url = environ.get('REQUEST_URI', base_url).split('?')[0].replace('autocreate', 'fileman') raw_login = None for oid_provider in configuration.user_openid_providers: openid_prefix = oid_provider.rstrip('/') + '/' if identity.startswith(openid_prefix): raw_login = identity.replace(openid_prefix, '') break if raw_login: openid_names.append(raw_login) # we should have the proxy file read... proxy_content = accepted['proxy_upload'][-1] # keep comment to a single line comment = accepted['comment'][-1].replace('\n', ' ') # single quotes break command line format - remove comment = comment.replace("'", ' ') user_dict = { 'short_id': uniq_id, 'full_name': full_name, 'organization': org, 'organizational_unit': org_unit, 'locality': locality, 'state': state, 'country': country, 'email': email, 'role': role, 'timezone': timezone, 'password': '', 'comment': '%s: %s' % ('Existing certificate', comment), 'openid_names': openid_names, } user_dict.update(oid_extras) # We must receive some ID from the provider if not uniq_id and not email: output_objects.append( {'object_type': 'error_text', 'text' : 'No ID information received!'}) if accepted.get('openid.sreg.required', '') and \ identity: # Stay on virtual host - extra useful while we test dual OpenID url = environ.get('REQUEST_URI', base_url).split('?')[0].replace('autocreate', 'logout') output_objects.append( {'object_type': 'text', 'text': '''Please note that sign-up for OpenID access does not work if you are already signed in with your OpenID provider - and that appears to be the case now. You probably have to reload this page after you explicitly '''}) output_objects.append( {'object_type': 'link', 'destination': url, 'target': '_blank', 'text': "Logout" }) return (output_objects, returnvalues.CLIENT_ERROR) if login_type == 'cert': user_dict['expire'] = int(time.time() + cert_valid_days * 24 * 60 * 60) try: distinguished_name_to_user(uniq_id) user_dict['distinguished_name'] = uniq_id except: output_objects.append({'object_type': 'error_text', 'text' : '''Illegal Distinguished name: Please note that the distinguished name must be a valid certificate DN with multiple "key=val" fields separated by "/". '''}) return (output_objects, returnvalues.CLIENT_ERROR) elif login_type == 'oid': user_dict['expire'] = int(time.time() + oid_valid_days * 24 * 60 * 60) fill_distinguished_name(user_dict) uniq_id = user_dict['distinguished_name'] # If server allows automatic addition of users with a CA validated cert # we create the user immediately and skip mail if login_type == 'cert' and configuration.auto_add_cert_user or \ login_type == 'oid' and configuration.auto_add_oid_user: fill_user(user_dict) logger.info('create user: %s' % user_dict) # Now all user fields are set and we can begin adding the user db_path = os.path.join(configuration.mig_server_home, user_db_filename) try: create_user(user_dict, configuration.config_file, db_path, ask_renew=False, default_renew=True) if configuration.site_enable_griddk and \ accepted['proxy_upload'] != ['']: # save the file, display expiration date proxy_out = handle_proxy(proxy_content, uniq_id, configuration) output_objects.extend(proxy_out) except Exception, err: logger.error('create failed for %s: %s' % (uniq_id, err)) output_objects.append( {'object_type': 'error_text', 'text' : '''Could not create the user account for you: Please report this problem to the grid administrators (%s).''' % \ admin_email}) return (output_objects, returnvalues.SYSTEM_ERROR) output_objects.append({'object_type': 'html_form', 'text' : '''Created the user account for you - please open <a href="%s">your personal page</a> to proceed using it. ''' % base_url}) return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) defaults = signature()[1] 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)
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) title_entry = find_entry(output_objects, 'title') title_entry['text'] = '%s send request' % \ configuration.short_title output_objects.append({'object_type': 'header', 'text' : '%s send request' % \ configuration.short_title}) target_id = client_id vgrid_name = accepted['vgrid_name'][-1].strip() visible_user_name = accepted['cert_id'][-1].strip() visible_res_name = accepted['unique_resource_name'][-1].strip() 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] valid_request_types = ['resourceowner', 'resourceaccept', 'vgridowner', 'vgridmember','vgridresource', 'vgridaccept', '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] if request_type == "plain": if not visible_user_name: output_objects.append({ 'object_type': 'error_text', 'text': 'No user ID specified!'}) return (output_objects, returnvalues.CLIENT_ERROR) user_id = visible_user_name anon_map = anon_to_real_user_map(configuration.user_home) if anon_map.has_key(visible_user_name): user_id = anon_map[visible_user_name] if not user_map.has_key(user_id): output_objects.append({'object_type': 'error_text', 'text': 'No such user: %s' % \ visible_user_name }) return (output_objects, returnvalues.CLIENT_ERROR) target_name = user_id user_dict = user_map[user_id] allow_vgrids = user_allowed_vgrids(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 = allow_vgrids else: email_vgrids = set(vgrids_allow_email).intersection(allow_vgrids) if any_vgrid in vgrids_allow_im: im_vgrids = allow_vgrids else: im_vgrids = set(vgrids_allow_im).intersection(allow_vgrids) 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!' % \ visible_user_name }) 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!' % \ visible_user_name }) 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!' % \ (visible_user_name, 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!' % \ visible_user_name}) return (output_objects, returnvalues.CLIENT_ERROR) target_list = [user_id] elif request_type == "vgridaccept": # Always allow accept messages but only between vgrid members/owners user_id = visible_user_name 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 not 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, configuration.site_vgrid_label)}) return (output_objects, returnvalues.CLIENT_ERROR) allow_vgrids = user_allowed_vgrids(configuration, client_id) if not vgrid_name in allow_vgrids: output_objects.append({ 'object_type': 'error_text', 'text': 'Invalid %s message! (%s sv %s)' % (request_type, user_id, allow_vgrids)}) return (output_objects, returnvalues.CLIENT_ERROR) target_id = '%s %s owners' % (vgrid_name, configuration.site_vgrid_label) target_name = vgrid_name target_list = [user_id] elif request_type == "resourceaccept": # Always allow accept messages between actual resource owners user_id = visible_user_name if not visible_res_name: output_objects.append({ 'object_type': 'error_text', 'text': 'No resource ID specified!'}) return (output_objects, returnvalues.CLIENT_ERROR) unique_resource_name = visible_res_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) owners_list = res_map[unique_resource_name][OWNERS] if not client_id in owners_list or not user_id in owners_list: output_objects.append({ 'object_type': 'error_text', 'text' : 'Invalid resource owner accept message!'}) return (output_objects, returnvalues.CLIENT_ERROR) target_id = '%s resource owners' % unique_resource_name target_name = unique_resource_name target_list = [user_id] elif request_type == "resourceowner": if not visible_res_name: output_objects.append({ 'object_type': 'error_text', 'text': 'No resource ID specified!'}) return (output_objects, returnvalues.CLIENT_ERROR) unique_resource_name = visible_res_name anon_map = anon_to_real_res_map(configuration.resource_home) if anon_map.has_key(visible_res_name): unique_resource_name = anon_map[visible_res_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' % \ visible_res_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) elif request_type in ["vgridmember", "vgridowner", "vgridresource"]: unique_resource_name = visible_res_name 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 if request_type != 'vgridresource': 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, configuration.site_vgrid_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, configuration.site_vgrid_label)}) return (output_objects, returnvalues.CLIENT_ERROR) # set target to resource and prevent repeated resource access requests if request_type == 'vgridresource': target_id = 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, configuration.site_vgrid_label)}) return (output_objects, returnvalues.CLIENT_ERROR) # Find all VGrid owners target_name = vgrid_name (status, target_list) = vgrid_list(vgrid_name, 'owners', configuration) if not status: output_objects.append({ 'object_type': 'error_text', 'text' : 'Could not load list of current owners for %s %s!' % (vgrid_name, configuration.site_vgrid_label)}) return (output_objects, returnvalues.CLIENT_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 for target in target_list: # 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} 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)