Migrate to ember 2.7 + correct jquery null context error + declare shim for popcorn (instead of silencing the JSHint error)
<?php
namespace CorpusParole\Libraries\Handle;
use Log;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Pagination\Paginator;
use phpseclib\Crypt\RSA;
// current_date = datetime.now()
// current_date_format = unicode(current_date.strftime('%Y-%m-%dT%H:%M:%SZ'))
// handle_record = {u'values': [
// {u'index': 1, u'ttl': 86400, u'type': u'URL', u'timestamp': current_date_format, u'data': {u'value': u'http://www.ribaenterprises.com', u'format': u'string'}},
// {u'index': 2, u'ttl': 86400, u'type': u'EMAIL', u'timestamp': current_date_format, u'data': {u'value': u'info@ribaenterprises.com', u'format': u'string'}},
// {u'index': 100, u'ttl': 86400, u'type': u'HS_ADMIN', u'timestamp': current_date_format, u'data': {u'value': {u'index': 200, u'handle': unicode(auth_id), u'permissions': u'011111110011'}, u'format': u'admin'}}
// ], u'handle': unicode(handle), u'responseCode': 1}
// class to handle communication with Handle server API.
// inspired by : https://github.com/theNBS/handleserver-samples
class HandleClient {
public function __construct($privateKeyOrCert, $pkpass, $adminId, $handleHost, $handlePort, $httpClient) {
$this->session = null;
$this->cert = null;
$this->adminId = $adminId;
$this->privateKeyRes = null;
$this->privateKeyOrCert = $privateKeyOrCert;
$this->pkpass = $pkpass;
$this->handleHost = $handleHost;
$this->handlePort = $handlePort;
$this->httpClient = $httpClient;
$this->guzzleOptions = ['verify' => false,];
}
public function __destruct () {
$this->close();
}
public function close() {
$this->deleteSession();
$this->freeResources();
}
private function getBaseUrl() {
return "https://$this->handleHost:$this->handlePort/api/";
}
private function getSessionAuthHeader() {
return "Handle sessionId=$this->session";
}
private function generateClientNonce() {
return openssl_random_pseudo_bytes(16);
}
private function getPrivateKeyRes() {
if(is_null($this->privateKeyRes)) {
$this->privateKeyRes = openssl_pkey_get_private($this->privateKeyOrCert, $this->pkpass);
}
return $this->privateKeyRes;
}
private function freeResources() {
if(!empty($this->privateKeyRes)) {
$privateKeyRes = $this->privateKeyRes;
$this->privateKeyRes = null;
openssl_free_key($privateKeyRes);
}
}
private function signBytesDsa($str) {
openssl_sign($str, $signature, $this->getPrivateKeyRes(), OPENSSL_ALGO_DSS1);
return $signature;
}
private function signBytesRsa($str) {
$rsa = new RSA();
$rsa->setHash('sha256');
if(!empty($this->pkpass)) {
$rsa->setPassword($this->pkpass);
}
$keyContent = $this->privateKeyOrCert;
if(is_file($keyContent)) {
$keyContent = file_get_contents($keyContent);
}
$rsa->loadKey($keyContent);
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
return $rsa->sign($str);
}
private function createAuthorisationHeaderFromJson($jsonresp) {
# Unpick number once (nonce) and session id from server response (this is the challenge)
$serverNonce = base64_decode($jsonresp['nonce']);
$this->sessionId = $jsonresp['sessionId'];
# Generate a client number once (cnonce)
$clientNonce = $this->generateClientNonce();
$clientNonceStr = base64_encode($clientNonce);
# Our response has to be the signature of server nonce + client nonce
$combinedNonce = $serverNonce . $clientNonce;
if($this->getPrivateKeyRes() === false) {
throw new \Exception("HandleClient: can not read private res");
}
$keyDetails = openssl_pkey_get_details($this->getPrivateKeyRes());
if($keyDetails === false) {
throw new \Exception("HandleClient: can not read private key");
}
if($keyDetails['type']===OPENSSL_KEYTYPE_RSA) {
$signature = $this->signBytesRsa($combinedNonce);
$signAlg = 'SHA256';
} elseif ($keyDetails['type']===OPENSSL_KEYTYPE_DSA) {
$signature = $this->signBytesDsa($combinedNonce);
$signAlg = 'SHA1';
} else {
throw new \Exception("HandleClient: $keyDetails[type] Format unknown");
}
$signatureStr = base64_encode($signature);
$this->freeResources();
# Build the authorisation header to send with the request
# Use SHA1 for DSA keys; SHA256 can be used for RSA keys
return $this->buildComplexAuthorizationString($signatureStr, $signAlg, $clientNonceStr);
}
private function buildComplexAuthorizationString($signatureString, $signAlg, $clientNonceString) {
return "Handle " .
"version=\"0\", " .
"sessionId=\"$this->sessionId\", " .
"cnonce=\"$clientNonceString\", " .
"id=\"$this->adminId\", " .
"type=\"HS_PUBKEY\", " .
"alg=\"$signAlg\", " .
"signature=\"$signatureString\"";
}
public function initSession() {
if(!empty($this->session) || !empty($this->cert)) {
return;
}
$headers = key_exists('headers', $this->guzzleOptions)?$this->guzzleOptions['headers']:[];
$headers = array_merge($headers, [
'Content-Type' => 'application/json;charset=UTF-8',
]);
$certContent = $this->privateKeyOrCert;
if(is_file($certContent)) {
$certContent = file_get_contents($certContent);
}
if(openssl_x509_parse($certContent) !== false) {
if(!empty($this->pkpass)) {
$this->cert = [$this->privateKeyOrCert, $this->pkpass];
} else {
$this->cert = $this->privateKeyOrCert;
}
$headers['Authorization'] = "Handle clientCert=\"true\"";
} else {
$url = $this->getBaseUrl()."sessions/";
$challengeRes = $this->httpClient->post($url, ['verify' => false]);
$jsonResp = json_decode($challengeRes->getBody(), true);
$pkheaders = [
'Content-Type' => 'application/json;charset=UTF-8',
'Authorization' => $this->createAuthorisationHeaderFromJson($jsonResp)
];
# Send the request again with a valid correctly signed Authorization header
$sessionResp = $this->httpClient->put($url.'this', ['headers' => $pkheaders, 'verify' => false]);
Log::debug('Create session with auth: '.$sessionResp->getStatusCode().' : '.$sessionResp->getReasonPhrase());
$jsonResp = json_decode($sessionResp->getBody(), true);
$this->session = $jsonResp['authenticated']?$jsonResp['sessionId']:"";
$headers['Authorization'] = "Handle version=\"0\", sessionId=\"$this->session\"";
}
$this->guzzleOptions = array_merge($this->guzzleOptions, ['headers' => $headers, 'cert' => $this->cert]);
}
// will call a async method. Apart logging we do not really care in the result
public function deleteSession() {
if(empty($this->session)) {
return;
}
$headers = [
'Content-Type' => 'application/json;charset=UTF-8',
'Authorization' => $this->getSessionAuthHeader()
];
$url = $this->getBaseUrl()."sessions/this";
// Do not really care of the response...
$this->httpClient->deleteAsync($url, ['headers' => $headers, 'verify' => false]);
$this->session = null;
}
/**
* Paginate all handle as a paginator.
*
* @param int $perPage
* @param string $pageName
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
public function paginateAll($prefix, $perPage = 15, $pageName = 'page', $page = null) {
$this->initSession();
$url = $this->getBaseUrl()."handles";
$params = [
'prefix' => $prefix,
'page' => is_null($page)?0:$page-1,
'pageSize' => $perPage
];
$paginateRes = $this->httpClient->get($url, array_merge($this->guzzleOptions, ['query' => $params]));
$paginateJson = json_decode($paginateRes->getBody(), true);
$total = (int)$paginateJson['totalCount'];
$results = $paginateJson['handles'];
return new LengthAwarePaginator($results, $total, $perPage, $page, [
'path' => Paginator::resolveCurrentPath(),
'pageName' => $pageName,
]);
}
public function deleteHandle($handle) {
$this->initSession();
if($handle === $this->adminId) {
throw new \Exception("HandleClient: can not delete admin handle");
}
$delUrl = $this->getBaseUrl()."handles/$handle";
$delRes = $this->httpClient->delete($delUrl, $this->guzzleOptions);
Log::debug('Delete Handle: '.$delRes->getStatusCode().': '.$delRes->getReasonPhrase());
}
public function createHandleUrlRecord($handle, $url) {
$this->initSession();
$currentDate = gmstrftime('%Y-%m-%dT%H:%M:%SZ');
$handleRecord = [
'values' => [
['index' => 1, 'ttl' => 86400, 'type' => 'URL', 'timestamp' => $currentDate, 'data' => ['value'=> $url, 'format'=> 'string']],
['index' => 100, 'ttl' => 86400, 'type' => 'HS_ADMIN', 'timestamp' => $currentDate, 'data' => [
'value' => ['index' => 200, 'handle' => $this->adminId],
'permissions' => '011111110011',
'format' => 'admin'
]
]
],
'handle' => $handle,
];
$submitUrl = $this->getBaseUrl()."handles/$handle";
$submitRes = $this->httpClient->put($submitUrl, array_merge($this->guzzleOptions, ['json' => $handleRecord, ]));
Log::debug('Create Handle Url: '.$submitRes->getStatusCode().' : '.$submitRes->getReasonPhrase());
}
}