Beispiel #1
0
function lookup_member_names($ma_url, $signer, $member_uuids)
{
    $client = XMLRPCClient::get_client($ma_url, $signer);
    // Exclude null/empty UIDS in member_uuids from our query
    $uids = array();
    foreach ($member_uuids as $uuid) {
        if (isset($uuid) && !is_null($uuid) && $uuid != '') {
            // If this is the portal's ID, skip it
            if ($uuid == get_portal_uid()) {
                //	error_log("Not looking up name for portal UID");
                continue;
            }
            $uids[] = $uuid;
            //    } else {
            // Like when an authority is the actor in a logged event
            //      error_log("lookup_member_names skipping an empty uid");
        }
    }
    $ids = array();
    if (sizeof($uids) > 0) {
        $options = array('match' => array('MEMBER_UID' => $uids), 'filter' => array('_GENI_IDENTIFYING_MEMBER_UID', '_GENI_MEMBER_DISPLAYNAME', 'MEMBER_FIRSTNAME', 'MEMBER_LASTNAME', 'MEMBER_EMAIL'));
        //error_log( " _lmns = " . print_r($member_uuids, true));
        // Replace the default result handler with one that will not
        // redirect to the error page on an error being returned.
        // This way we can continue loading pages that use this
        // Although we get a name of NONE for all members the user asked about
        // on an error
        global $put_message_result_handler;
        $put_message_result_handler = 'no_redirect_result_handler';
        $options = array_merge($options, $client->options());
        $res = $client->lookup_identifying_member_info($client->creds(), $options);
        $put_message_result_handler = null;
        if (isset($res) && !is_null($res)) {
            foreach ($res as $member_urn => $member_info) {
                $member_uuid = $member_info['_GENI_IDENTIFYING_MEMBER_UID'];
                $displayName = $member_info['_GENI_MEMBER_DISPLAYNAME'];
                $lastName = $member_info['MEMBER_LASTNAME'];
                $firstName = $member_info['MEMBER_FIRSTNAME'];
                $email = $member_info['MEMBER_EMAIL'];
                if ($displayName) {
                    $ids[$member_uuid] = $displayName;
                } else {
                    if ($lastName && $firstName) {
                        $ids[$member_uuid] = "{$firstName} {$lastName}";
                    } else {
                        if ($email) {
                            $ids[$member_uuid] = $email;
                        } else {
                            parse_urn($member_urn, $authority, $type, $username);
                            $ids[$member_uuid] = $username;
                        }
                    }
                }
            }
        }
    }
    // Federation API apparently doesn't give a return entry for a UID it doesn't know about,
    // since it can't make up a URN
    // But clients expect some entry for each ID they query for
    foreach ($member_uuids as $uuid) {
        if (!array_key_exists($uuid, $ids)) {
            $ids[$uuid] = "NONE";
        }
    }
    return $ids;
}
}
$certNode = $cert_nodes->item(0);
$pemCert = $certNode->nodeValue;
$beginpem = "-----BEGIN CERTIFICATE-----\n";
$endpem = "-----END CERTIFICATE-----\n";
$signer_urn = pem_cert_geni_urn($beginpem . $pemCert . $endpem);
/* Test if the signer is from the same GENI Authority as the portal.
   We really would like to compare the signer to the authenticated
   user, but in the speaks-for world we have no way of getting the
   user info without the speaks-for credential. This is an attempt to
   weed out obviously bad signers, while not necessarily an indication
   that the speaks-for credential will work.
*/
$tool = Portal::getInstance();
$tool_pem = $tool->certificate();
$tool_urn = pem_cert_geni_urn($tool_pem);
parse_urn($tool_urn, $tool_authority, $tool_type, $tool_name);
parse_urn($signer_urn, $signer_authority, $signer_type, $signer_name);
if ($signer_authority != $tool_authority) {
    header('HTTP/1.1 406 Not Acceptable');
    exit;
}
/* Now put the credential in the database. */
$db_result = store_speaks_for($key_token, $raw_cred, $signer_urn, $expires);
if (!$db_result) {
    header('HTTP/1.1 500 Cannot store uploaded credential');
    exit;
}
// All done. Signal success without passing any content.
$_SESSION['lastmessage'] = "You succesfully authorized the GENI Portal";
header('HTTP/1.1 204 No Content');
Beispiel #3
0
function invoke_omni_function($am_urls, $user, $args, $slice_users = array(), $bound_rspec = 0, $stitch_rspec = 0, $fork = false, $omni_invocation_dir = NULL, $api_version = "2")
{
    global $portal_gcf_dir;
    /* $file_manager only holds on to non-critical files (i.e., those
       that can be deleted regardless of whether the call was successful
       or not). */
    $file_manager = new FileManager();
    //  error_log("INVOKE : " . print_r($am_urls, true));
    // If we get a single URL, make it an array (handle the general case)
    if ($am_urls && !is_array($am_urls)) {
        $am_urls = array($am_urls);
    }
    /* Does each given URL handle speaks-for?
            If one or more AM URLs are given, check the SR for whether SF is enabled.
            If no AM URL is given but it's for stitching, just assume for now
                that all AMs handle SpeaksFor and see what happens.
      */
    if ($am_urls) {
        $handles_speaks_for = True;
        foreach ($am_urls as $am_url) {
            $am_handles_speaks_for = lookup_attribute($am_url, SERVICE_ATTRIBUTE_SPEAKS_FOR) == 't';
            if (!$am_handles_speaks_for) {
                $handles_speaks_for = False;
                break;
            }
        }
    } else {
        $handles_speaks_for = True;
    }
    /*
     If an aggregate doesn't handle speaks-for, 
     we use the inside cert and key of the user
     If an aggregate DOES handle speaks-for and the
     user has a speaks-for credential, 
     portal's cert and key and pass along the geni_speaking_for option
    */
    $speaks_for_invocation = false;
    $cert = $user->insideCertificate();
    $private_key = $user->insidePrivateKey();
    $speaks_for_cred = $user->speaksForCred();
    if ($handles_speaks_for and $speaks_for_cred) {
        $speaks_for_invocation = true;
        $cert = $user->certificate();
        $private_key = $user->privateKey();
    }
    $username = $user->username;
    $urn = $user->urn();
    // Get the authority from the user's URN
    parse_urn($urn, $authority, $type, $name);
    $aggregates = "aggregates=";
    $first = True;
    // get AMs if non-stitchable
    if (!$stitch_rspec) {
        if (is_array($am_urls)) {
            if (count($am_urls) == 0) {
                error_log("am_client Got non stitching RSpec and 0 AM URLs");
                // Careful: Are all RSpecs that stitcher can handle marked as stitch_rspecs properly?
                // return("Invalid AM URL");
            }
            foreach ($am_urls as $single) {
                if (!isset($single) || is_null($single) || $single == '') {
                    error_log("am_client cannot invoke Omni with invalid AM URL");
                    return "Invalid AM URL";
                }
                if ($first) {
                    $first = False;
                } else {
                    $aggregates = $aggregates . ", ";
                }
                $aggregates = $aggregates . $single;
            }
            $aggregates = $aggregates . "\n";
        } elseif (!isset($am_url) || is_null($am_url) || $am_url == '') {
            error_log("am_client cannot invoke Omni without an AM URL");
            return "Missing AM URL";
        }
    }
    /* Create a directory to store all temp files, including logs and error
          messages *if one doesn't exist already*. Let the prefix be the username.
          An "omni invocation ID" is created.
       
          Returns something like: /tmp/omni-invoke-myuser-RKvQ1Z
       */
    if (is_null($omni_invocation_dir)) {
        $omni_invocation_dir = createTempDir($username);
    }
    /* Write key and credential files */
    $tmp_version_cache = "{$omni_invocation_dir}/omniVersionCache";
    $tmp_agg_cache = "{$omni_invocation_dir}/omniAggCache";
    $file_manager->add($tmp_version_cache);
    $file_manager->add($tmp_agg_cache);
    $cert_file = writeDataToTempDir($omni_invocation_dir, $cert, OMNI_INVOCATION_FILE::CERTIFICATE_FILE);
    $file_manager->add($cert_file);
    $key_file = writeDataToTempDir($omni_invocation_dir, $private_key, OMNI_INVOCATION_FILE::PRIVATE_KEY_FILE);
    $file_manager->add($key_file);
    $slice_users = $slice_users + array($user);
    $username_array = array();
    $all_ssh_key_files = array();
    $ssh_key_files_by_user = array();
    foreach ($slice_users as $slice_user) {
        $slice_urn = $slice_user->urn();
        $ssh_key_files = write_ssh_keys($slice_user, $user, $omni_invocation_dir);
        // Skip from omni_config any user with no public SSH keys
        if (count($ssh_key_files) == 0) {
            continue;
        }
        $all_ssh_key_files = array_merge($all_ssh_key_files, $ssh_key_files);
        $ssh_key_files_by_user[$slice_urn] = $ssh_key_files;
        $username_array[] = $slice_user->username;
    }
    /* Create OMNI config file */
    if (!isset($sa_url)) {
        $sa_url = get_first_service_of_type(SR_SERVICE_TYPE::SLICE_AUTHORITY);
    }
    if (!isset($ma_url)) {
        $ma_url = get_first_service_of_type(SR_SERVICE_TYPE::MEMBER_AUTHORITY);
    }
    $omni_config = "[omni]\n" . "default_cf = my_chapi\n" . "users = " . implode(", ", $username_array) . "\n";
    // As of omni 2.7, by default omni queries the CH to get slice members
    // We pass those in explicitly and don't want the extra query
    $omni_config .= "useslicemembers = False\n";
    // specify AM for non-stitchable RSpecs
    if (!$stitch_rspec) {
        if (is_array($am_urls)) {
            $omni_config = $omni_config . $aggregates . "\n";
        }
    }
    # Create the aggregate nickname cache file
    if (!write_agg_nick_cache($tmp_agg_cache)) {
        error_log("Failed to write the aggregate nickname cache.");
        // Now what? Continue?
    }
    // FIXME: Get the /CH URL from a portal/www/portal/settings.php entry?
    $omni_config = $omni_config . "[my_chapi]\n" . "type=chapi\n" . "authority={$authority}\n" . "ch=https://{$authority}:8444/CH\n" . "sa={$sa_url}\n" . "ma={$ma_url}\n" . "cert={$cert_file}\n" . "key={$key_file}\n";
    foreach ($slice_users as $slice_user) {
        $slice_username = $slice_user->username;
        $slice_urn = $slice_user->urn();
        if (!array_key_exists($slice_urn, $ssh_key_files_by_user)) {
            // This user had no SSH keys
            continue;
        }
        $all_key_files = implode(',', $ssh_key_files_by_user[$slice_urn]);
        $omni_config = $omni_config . "[{$slice_username}]\n" . "urn={$slice_urn}\n" . "keys={$all_key_files}\n";
    }
    foreach ($all_ssh_key_files as $ssh_key_file) {
        $file_manager->add($ssh_key_file);
    }
    $omni_file = writeDataToTempDir($omni_invocation_dir, $omni_config, OMNI_INVOCATION_FILE::OMNI_CONFIGURATION_FILE);
    $file_manager->add($omni_file);
    /* Call OMNI */
    $omni_log_file = "{$omni_invocation_dir}/" . OMNI_INVOCATION_FILE::DEBUG_LOG_FILE;
    $omni_stderr_file = "{$omni_invocation_dir}/" . OMNI_INVOCATION_FILE::ERROR_LOG_FILE;
    $omni_stdout_file = "{$omni_invocation_dir}/" . OMNI_INVOCATION_FILE::CALL_RESULTS_FILE;
    $omni_command_file = "{$omni_invocation_dir}/" . OMNI_INVOCATION_FILE::COMMAND_FILE;
    $omni_pid_file = "{$omni_invocation_dir}/" . OMNI_INVOCATION_FILE::PID_FILE;
    /*    $cmd_array = array($portal_gcf_dir . '/src/omni.py', */
    $cmd_array = array($portal_gcf_dir . '/src/stitcher_php.py', '-c', $omni_file, '-l', write_logger_configuration_file($omni_invocation_dir), '--logoutput', $omni_log_file, '--timeout', '45', '--api-version', $api_version, "--GetVersionCacheName", $tmp_version_cache, "--ForceUseAggNickCache", "--AggNickCacheName", $tmp_agg_cache);
    $descriptor_spec = array(0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("file", $omni_stderr_file, "a"));
    /* stitcher.py: specify fileDir */
    $cmd_array[] = '--fileDir';
    $cmd_array[] = $omni_invocation_dir;
    // specify AM for non-stitchable RSpecs
    if (!$stitch_rspec) {
        foreach ($am_urls as $am_url) {
            $cmd_array[] = '-a';
            $cmd_array[] = $am_url;
        }
    }
    if ($speaks_for_invocation) {
        $cmd_array[] = "--speaksfor=" . $user->urn;
        $speaks_for_cred_filename = writeDataToTempDir($omni_invocation_dir, $speaks_for_cred->credential(), OMNI_INVOCATION_FILE::SPEAKSFOR_CREDENTIAL_FILE);
        $file_manager->add($speaks_for_cred_filename);
        $cmd_array[] = "--cred=" . $speaks_for_cred_filename;
    }
    for ($i = 0; $i < count($args); $i++) {
        $cmd_array[] = $args[$i];
    }
    $command = implode(" ", $cmd_array);
    // save command that was run
    $cmd_file = fopen($omni_command_file, "a");
    fwrite($cmd_file, $command);
    fclose($cmd_file);
    /* forked omni call */
    if ($fork) {
        // define how to handle streams
        $stdout_redirect = " > " . $omni_stdout_file;
        $stderr_redirect = " 2> " . $omni_stderr_file;
        $stdin_redirect = " < /dev/null";
        // set up call via nohup and grab its PID
        $fork_call = 'nohup ' . $command . $stdout_redirect . $stderr_redirect . $stdin_redirect . ' & echo $!';
        error_log("am_client invoke_omni_function COMMAND = " . $fork_call);
        exec($fork_call, $op);
        // assuming success, $op will be a non-empty array
        if ($op) {
            // nohup should return an array with one line containing the PID
            $pid = $op[0];
            // write PID to a file
            $pid_file = fopen($omni_pid_file, "a");
            fwrite($pid_file, $pid);
            fclose($pid_file);
            // FIXME: Should we wait around to do 'ps -p <pid>' to make sure
            // process didn't quickly die?
            return $pid;
        } else {
            return NULL;
        }
    } else {
        error_log("am_client invoke_omni_function COMMAND = " . $command);
        $handle = proc_open($command, $descriptor_spec, $pipes);
        stream_set_blocking($pipes[1], 0);
        // 1 MB
        $bufsiz = 1024 * 1024;
        $output = '';
        $outchunk = null;
        //time to terminate omni process
        $now = time();
        $kill_time = $now + AM_CLIENT_OMNI_KILL_TIME;
        while ($outchunk !== FALSE && !feof($pipes[1]) && $now < $kill_time) {
            $outchunk = fread($pipes[1], $bufsiz);
            if ($outchunk != null && $outchunk !== FALSE) {
                $output = $output . $outchunk;
                $usleep = 0;
            } else {
                // 0.25 seconds
                $usleep = 250000;
            }
            // If we got data, don't sleep, see if there's more ($usleep = 0)
            // If no data, sleep for a little while then check again.
            usleep($usleep);
            $now = time();
        }
        // Catch any final output after timeout
        $outchunk = fread($pipes[1], $bufsiz);
        if ($outchunk != null && $outchunk !== FALSE) {
            $output = $output . $outchunk;
        }
        //fclose($pipes[0]);
        //fclose($pipes[1]);
        //proc_close($handle);
        $status = proc_get_status($handle);
        if (!$status['running']) {
            fclose($pipes[0]);
            fclose($pipes[1]);
            $return_value = $status['exitcode'];
            proc_close($handle);
        } else {
            // Still running, terminate it.
            // See https://bugs.php.net/bug.php?id=39992, for problems
            // terminating child processes and a workaround involving posix_setpgid()
            fclose($pipes[0]);
            fclose($pipes[1]);
            $term_result = proc_terminate($handle);
            // Omni is taking too long to respond so
            // assign Timeout error message to output and this message may show up in UI
            //msg constant defined above
            $output = AM_CLIENT_TIMED_OUT_MSG;
        }
        /*
        unlink($cert_file);
        unlink($key_file);
        unlink($omni_file);
        unlink($tmp_version_cache);
        unlink($tmp_agg_cache);
        foreach ($all_ssh_key_files as $tmpfile) {
          unlink($tmpfile);
        }
        if ($speaks_for_invocation) {
          unlink($speaks_for_cred_filename);
        }
        */
        // Good for debugging but verbose
        //     error_log("am_client output " .  print_r($output, True));
        // FIXME: Write stdout's contents to omni_stdout_file for now to capture
        //  stitcher output. This will be changed when assigning descriptor_spec
        //  to send to a file rather than a pipe.
        $stdout_file = fopen($omni_stdout_file, "a");
        fwrite($stdout_file, $output);
        fclose($stdout_file);
        $output2 = json_decode($output, True);
        if (is_null($output2)) {
            // this is probably a traceback from python
            // return it as a string
            // but see if omni-stderr exists, and pass back its information
            // in addition to output to get a better traceback
            $error_file = fopen($omni_stderr_file, "r");
            // only try to read if fopen was successful and if the error file
            // contains something (i.e. more than 0 bytes)
            if ($error_file && filesize($omni_stderr_file)) {
                $error_file_contents = fread($error_file, filesize($omni_stderr_file));
                if ($error_file_contents) {
                    $substr = $error_file_contents;
                    if (strlen($substr) > 120) {
                        // Pull out just the interesting bits: match on either OmniError or StitchingError
                        $substr = trim(preg_replace("/Traceback(.*)OmniError\\:/s", "", $substr, -1));
                        $substr = trim(preg_replace("/Traceback(.*)StitchingError\\:/s", "", $substr, -1));
                        $substr = "..." . $substr;
                    }
                    error_log("am_client invoke_omni_function: " . "stderr file non-empty: '" . $substr . "'. Check " . $omni_stderr_file . " for more information");
                    // uncomment the next line to append stderr contents to what
                    // users will see. But this has been fixed elsewhere under ticket 1086.
                    //$output .= $error_file_contents;
                }
                fclose($error_file);
            }
            error_log("am_client invoke_omni_function:" . "JSON result is not parseable: \"{$output}\"");
            return $output;
        }
        /* Clean out $file_manager's directory 
              This does NOT include log/error files or any additional files that
              stitching requests may make.
           */
        $file_manager->destruct();
        /* Delete the remaining temp files only if the decoded output is an array
             and its length is 2 and the second value (index 1) is boolean true
             (not null or empty string).
           */
        if (is_array($output2) && count($output2) == 2 && $output2[1]) {
            clean_directory($omni_invocation_dir);
            rmdir($omni_invocation_dir);
            //unlink($omni_log_file);
            //unlink($omni_stderr_file);
        }
        //error_log("Returning output2 : " . print_r($output2, True));
        return $output2;
    }
}