xaizek / rocketgit (License: AGPLv3+) (since 2018-12-09)
Light and fast Git hosting solution suitable to serve both as a hub or as a personal code storage with its tickets, pull requests, API and much more.
<root> / inc / wh / amazon.inc.php (049f76bd9ae413df8401d158b1327860e8e9724d) (9,152B) (mode 100644) [raw]
<?php

/*
 * Builds the signature for a request
 */
function rg_amazon_auth($a)
{
	rg_prof_start('amazon_auth');
	rg_log_enter('amazon_auth');
	rg_log_ml('a: ' . print_r($a, TRUE));

	$ret = array('ok' => 0);
	while (1) {
		$iheaders_final = array();
		$iheaders_list = array();
		foreach ($a['iheaders'] as $head => $val) {
			$iheaders_final[] = strtolower($head)
				. ':' . trim($val);
			$iheaders_list[] = strtolower($head);
		}
		asort($iheaders_list);
		asort($iheaders_final);
		$iheaders_list = implode(';', $iheaders_list);
		$iheaders_final = implode("\n", $iheaders_final) . "\n\n";

		$canonical_request = $a['method'] . "\n"
			. '/' . urlencode($a['file']) . "\n"
			. "\n"
			. $iheaders_final
			. $iheaders_list . "\n"
			. bin2hex($a['x-amz-content-sha256']);
		rg_log_ml('canonical_request:' . "\n" . $canonical_request
			. "\n" . '===');

		$string_to_sign = 'AWS4-HMAC-SHA256' . "\n"
			. $a['x-amz-date'] . "\n"
			. gmdate('Ymd', $a['ts'])
				. '/' . $a['region']
				. '/' . $a['service']
				. '/' . 'aws4_request'
				. "\n"
			. hash('sha256', $canonical_request);
		rg_log_ml('string_to_sign:' . "\n" . $string_to_sign
			. "\n" . '===');

		$date_key = hash_hmac('sha256', gmdate('Ymd', $a['ts']),
			'AWS4' . $a['secret_access_key'], TRUE);
		$date_region_key = hash_hmac('sha256', $a['region'],
			$date_key, TRUE);
		$date_region_service_key = hash_hmac('sha256',
			$a['service'], $date_region_key, TRUE);
		$signing_key = hash_hmac('sha256', 'aws4_request',
			$date_region_service_key, TRUE);
		$signature = hash_hmac('sha256', $string_to_sign, $signing_key);
		rg_log('DEBUG: signature=' . $signature);

		$cred = $a['access_key_id']
			. '/' . gmdate('Ymd')
			. '/' . $a['region']
			. '/' . $a['service']
			. '/aws4_request';

		$ret['data'] = 'AWS4-HMAC-SHA256'
			. ' Credential=' . $cred
			. ', SignedHeaders=' . $iheaders_list
			. ', Signature=' . $signature;
		$ret['ok'] = 1;
		break;
	}

	rg_log_exit();
	rg_prof_end('amazon_auth');
	return $ret;
}

/*
 * Make a generic request to the amazon
 */
function rg_amazon_req($a)
{
	rg_prof_start('amazon_req');
	rg_log_enter('amazon_req');
	rg_log_ml('DEBUG: a:' . print_r($a, TRUE));

	$ret = array('ok' => 0);
	while (1) {
		$url = 'https://' . $a['host'] . '/' . urlencode($a['file']);
		$c = curl_init($url);
		if ($c === FALSE) {
			$ret['error'] = 'cannot init curl';
			break;
		}

		$a['region'] = trim(strtolower($a['region']));

		$a['ts'] = time();
		$a['x-amz-date'] = gmdate('Ymd', $a['ts'])
			. 'T' . gmdate('His', $a['ts']) . 'Z';
		$a['x-amz-content-sha256'] = hash('sha256', $a['content'], TRUE);
		$a['host'] = trim(strtolower($a['host']));
		$a['service'] = trim(strtolower($a['service']));

		if (!isset($a['iheaders']))
			$a['iheaders'] = array();
		$a['iheaders']['Host'] = $a['host'];
		$a['iheaders']['x-amz-date'] = $a['x-amz-date'];
		$a['iheaders']['x-amz-content-sha256'] = bin2hex($a['x-amz-content-sha256']);

		$auth = rg_amazon_auth($a);
		if ($auth['ok'] != 1) {
			$ret['error'] = $auth['error'];
			break;
		}

		$headers = array();
		if (isset($a['content_type']))
			$headers[] = 'Content-Type: ' . $a['content_type'];
		$headers[] = 'Authorization: ' . $auth['data'];
		foreach ($a['iheaders'] as $head => $val)
			$headers[] = $head . ': ' . $val;
		//rg_log_ml('HEADERS:' . print_r($headers, TRUE));

		curl_setopt($c, CURLOPT_CUSTOMREQUEST, $a['method']);
		//curl_setopt($c, CURLOPT_POST, 1);
		curl_setopt($c, CURLOPT_POSTFIELDS, $a['content']);
		curl_setopt($c, CURLOPT_RETURNTRANSFER, TRUE);
		curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
		//curl_setopt($c, CURLOPT_HEADER, 1);
		curl_setopt($c, CURLOPT_HTTPHEADER, $headers);
		curl_setopt($c, CURLOPT_USERAGENT, 'RocketGit Cloud client');
		curl_setopt($c, CURLOPT_CONNECTTIMEOUT, 30);
		curl_setopt($c, CURLOPT_ENCODING, ''); // => use all methods
		curl_setopt($c, CURLOPT_VERBOSE, TRUE);
		curl_setopt($c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
		//curl_setopt($c, CURLOPT_CERTINFO, TRUE);

		$err = @fopen('php://temp', 'w');
		if ($err !== FALSE)
			curl_setopt($c, CURLOPT_STDERR, $err);

		$r = curl_exec($c);
		rg_log_ml('DEBUG: curl_exec returned: ' . $r);

		if ($err !== FALSE) {
			rewind($err);
			$xerr = @fread($err, 16 * 4096);
			fclose($err);
			$ret['debug'] = $xerr;
			rg_log_ml('xerr=' . $xerr);
		}

		if ($r === FALSE) {
			$ret['error'] = curl_error($c);

			$_info = curl_getinfo($c);
			rg_log_ml('Debug: ' . print_r($_info, TRUE));
			break;
		}

		$_info = curl_getinfo($c);
		rg_log_ml('Debug: ' . print_r($_info, TRUE));

		if (($_info['http_code'] != 200)
			&& ($_info['http_code'] != 301)) {
			$ret['error'] = $r;
			break;
		}

		$ret['ok'] = 1;
		$ret['answer'] = $r;
		break;
	}
	curl_close($c);

	rg_log_exit();
	rg_prof_end('amazon_req');
	return $ret;
}

/*
 *
 * @bucket contains the domain part (s3.amazonaws.com for example)
 */
function rg_amazon_s3_put_object($a, $content)
{
	rg_prof_start('amazon_s3_put_object');
	rg_log_enter('amazon_s3_put_object');
	rg_log_ml('a: ' . print_r($a, TRUE));

	$ret = array('ok' => 0);
	while (1) {
		$a['service'] = 's3';
		$a['method'] = 'PUT';
		$a['host'] = $a['bucket'] . '.' . $a['service'] . '.'
			. $a['region'] . '.amazonaws.com';
		$a['content'] = $content;

		$ret = rg_amazon_req($a);
		break;
	}

	rg_log_exit();
	rg_prof_end('amazon_s3_put_object');
	return $ret;
}

/*
 * Create a code deploy
 * http://docs.aws.amazon.com/codedeploy/latest/APIReference/API_CreateDeployment.html
 */
function rg_amazon_codedeploy_create($a)
{
	rg_prof_start('amazon_codedeploy_create');
	rg_log_ml_enter('amazon_codedeploy_create');

	if (empty($a['deployment_config_name']))
		$a['deployment_config_name'] = 'CodeDeployDefault.OneAtATime';

	// do the codedeploy - we may want to add "version" to "s3Location"
	$json = '{'
		.	'"applicationName": "' . rg_json_escape($a['application_name']) . '"'
		.	', "deploymentGroupName": "' . rg_json_escape($a['deployment_group_name']) . '"'
		.	', "description": "test description"'
		.	', "deploymentConfigName": "' . rg_json_escape($a['deployment_config_name']) . '"'
		.	', "ignoreApplicationStopFailures": ' . (strchr($a['flags'], 'I') ? 'true' : 'false')
		.	', "revision": {'
		.		'"revisionType": "S3"'
		.		', "s3Location": {'
		.			'"bundleType": "zip"'
		.			', "bucket": "' . rg_json_escape($a['bucket']) . '"'
		.			', "key": "' . rg_json_escape($a['file']) . '"'
		.		'}'
		.	'}'
		. '}';

	$ret = array('ok' => 0);
	while (1) {
		$a['service'] = 'codedeploy';
		$a['method'] = 'POST';
		$a['file'] = '';
		$a['host'] = $a['service'] . '.' . $a['region'] . '.amazonaws.com';
		$a['iheaders'] = array('x-amz-target' => 'CodeDeploy_20141006.CreateDeployment');
		$a['content'] = $json;
		$a['content_type'] = 'application/x-amz-json-1.1';

		$ret = rg_amazon_req($a);
		break;
	}

	rg_log_exit();
	rg_prof_end('amazon_codedeploy_create');
	return $ret;
}

/*
 * Calls an Amazon Lambda function
 * http://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html
 */
function rg_amazon_lambda_invoke($a)
{
	rg_prof_start('amazon_lambda_invoke');
	rg_log_ml_enter('amazon_lambda_invoke');

	if (!isset($a['invocation_type']))
		$a['invocation_type'] = 'RequestResponse';

	$ret = array('ok' => 0);
	while (1) {
		$a['service'] = 'lambda';
		$a['method'] = 'POST';
		$a['file'] = '2015-03-31/functions/' . $a['function']
			. '/invocations';
		$a['host'] = $a['service'] . '.' . $a['region'] . '.amazonaws.com';
		$a['iheaders'] = array(
			'x-Amz-Client-Context' => '',
			'x-Amz-Invocation-Type' => $a['invocation_type'],
			'x-Amz-Log-Type' => 'Tail');

		$ret = rg_amazon_req($a);
		break;
	}

	rg_log_exit();
	rg_prof_end('amazon_lambda_invoke');
	return $ret;
}

/*
 * Generic cosmetic for Amazon
 */
function rg_wh_amazon_cosmetic(&$row)
{
	$a = $row['idata']['access_key_id'];
	$row['idata']['HTML:access_key_id_secure'] =
		rg_xss_safe(substr($a, 0, 1) . '...' . substr($a, -1, 1));

	$a = $row['idata']['secret_access_key'];
	$row['idata']['HTML:secret_access_key_secure'] =
		rg_xss_safe(substr($a, 0, 1) . '...' . substr($a, -1, 1));
}

/*
 * Generic fill_vars for Amazon
 */
function rg_wh_amazon_fill_vars(&$a)
{
	$a['access_key_id'] = trim(rg_var_str('wh::idata::access_key_id'));
	$a['secret_access_key'] = trim(rg_var_str('wh::idata::secret_access_key'));
	$a['region'] = trim(rg_var_str('wh::idata::region'));
}

/*
 * Generic validation for Amazon
 */
function rg_wh_amazon_validate_vars($a, &$errmsg)
{
	$ret = FALSE;
	while (1) {
		if (empty($a['access_key_id'])) {
			$errmsg[] = rg_template('user/settings/wh/amazon/inv_access_key_id.txt',
				$rg, TRUE /*xss*/);
			break;
		}

		if (empty($a['secret_access_key'])) {
			$errmsg[] = rg_template('user/settings/wh/amazon/inv_secret_access_key.txt',
				$rg, TRUE /*xss*/);
			break;
		}

		if (empty($a['region'])) {
			$errmsg[] = rg_template('user/settings/wh/amazon/inv_region.txt',
				$rg, TRUE /*xss*/);
			break;
		}

		$ret = TRUE;
		break;
	}

	return $ret;
}

/*
 * Generic default_paras for Amazon
 */
function rg_wh_amazon_default_paras(&$a)
{
	$a['region'] = '';
	$a['access_key_id'] = '';
	$a['secret_access_key'] = '';
}

?>
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/rocketgit

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/rocketgit

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master