diff options
Diffstat (limited to 'vendor/edu-sharing-plugin')
| -rw-r--r-- | vendor/edu-sharing-plugin/edu-sharing-auth-helper.php | 107 | ||||
| -rw-r--r-- | vendor/edu-sharing-plugin/edu-sharing-helper-abstract.php | 43 | ||||
| -rw-r--r-- | vendor/edu-sharing-plugin/edu-sharing-helper-base.php | 45 | ||||
| -rw-r--r-- | vendor/edu-sharing-plugin/edu-sharing-helper.php | 47 | ||||
| -rw-r--r-- | vendor/edu-sharing-plugin/edu-sharing-node-helper.php | 184 | ||||
| -rw-r--r-- | vendor/edu-sharing-plugin/example/example-api.php | 59 | ||||
| -rw-r--r-- | vendor/edu-sharing-plugin/example/example.php | 49 | ||||
| -rw-r--r-- | vendor/edu-sharing-plugin/example/index.html | 120 | ||||
| -rw-r--r-- | vendor/edu-sharing-plugin/readme.md | 1 |
9 files changed, 0 insertions, 655 deletions
diff --git a/vendor/edu-sharing-plugin/edu-sharing-auth-helper.php b/vendor/edu-sharing-plugin/edu-sharing-auth-helper.php deleted file mode 100644 index ef9dec1..0000000 --- a/vendor/edu-sharing-plugin/edu-sharing-auth-helper.php +++ /dev/null @@ -1,107 +0,0 @@ -<?php -require_once "edu-sharing-helper-abstract.php"; - -class EduSharingAuthHelper extends EduSharingHelperAbstract { - - /** - * Gets detailed information about a ticket - * Will throw an exception if the given ticket is not valid anymore - * @param string $ticket - * The ticket, obtained by @getTicketForUser - * @return array - * Detailed information about the current session - * @throws Exception - * Thrown if the ticket is not valid anymore - */ - public function getTicketAuthenticationInfo(string $ticket) { - $curl = curl_init($this->base->baseUrl . '/rest/authentication/v1/validateSession'); - curl_setopt_array($curl, [ - CURLOPT_HTTPHEADER => [ - $this->getRESTAuthenticationHeader($ticket), - 'Accept: application/json', - 'Content-Type: application/json', - ], - CURLOPT_RETURNTRANSFER => 1, - CURLOPT_CONNECTTIMEOUT => 5, - CURLOPT_TIMEOUT => 5 - ]); - if ($this->base->http_proxy) { - curl_setopt($curl, CURLOPT_PROXY, $this->base->http_proxy); - } - $data = json_decode(curl_exec($curl), true); - curl_close($curl); - if ( is_null( $data ) ) { - throw new Exception( 'No answer from repository. Possibly a timeout while trying to connect' ); - } - if($data['statusCode'] !== 'OK') { - throw new Exception('The given ticket is not valid anymore'); - } - return $data; - } - - /** - * Fetches the edu-sharing ticket for a given username - * @param string $username - * The username you want to generate a ticket for - * @return string - * The ticket, which you can use as an authentication header, see @getRESTAuthenticationHeader - * @throws Exception - */ - public function getTicketForUser(string $username, $bodyparams = null) { - if ($bodyparams === null) { - $bodyparams = [ - "primaryAffiliation" => "employee", - "skills" => [ - "string" - ], - "types" => [ - "string" - ], - "extendedAttributes" => [ - 'affiliation' => ["employee"] - ], - "vcard" => "string", - "firstName" => User::findCurrent()->vorname, - "lastName" => User::findCurrent()->nachname, - "email" => User::findCurrent()->email, - "avatar" => "string", - "about" => "string" - ]; - } - $curl = curl_init($this->base->baseUrl . '/rest/authentication/v1/appauth/' . rawurlencode($username)); - curl_setopt_array($curl, [ - CURLOPT_POST => 1, - CURLOPT_FAILONERROR => false, - CURLOPT_RETURNTRANSFER => 1, - CURLOPT_HTTPHEADER => $this->getSignatureHeaders($username), - CURLOPT_CONNECTTIMEOUT => 5, - CURLOPT_TIMEOUT => 5 - ]); - curl_setopt( - $curl, - CURLOPT_POSTFIELDS, - is_array($bodyparams) ? json_encode($bodyparams) : (string) $bodyparams - ); - if ($this->base->http_proxy) { - curl_setopt($curl, CURLOPT_PROXY, $this->base->http_proxy); - } - - $output = curl_exec($curl); - $data = json_decode($output, true); - - $err = curl_errno( $curl ); - $info = curl_getinfo($curl); - curl_close($curl); - if ($err === 0 && $info["http_code"] === 200 && $data['userId'] === $username) { - return $data['ticket']; - } else { - if ( is_null( $data ) ) { - $data = ['error' => $output]; - } - throw new Exception( - 'edu-sharing ticket could not be retrieved: HTTP-Code ' . - $info["http_code"] . ': ' . $data['error'] - ); - } - } -} diff --git a/vendor/edu-sharing-plugin/edu-sharing-helper-abstract.php b/vendor/edu-sharing-plugin/edu-sharing-helper-abstract.php deleted file mode 100644 index d531eec..0000000 --- a/vendor/edu-sharing-plugin/edu-sharing-helper-abstract.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -abstract class EduSharingHelperAbstract { - protected $base; - public function __construct( - EduSharingHelperBase $base - ) { - $this->base = $base; - } - - /** - * Generates the header to use for a given ticket to authenticate with any edu-sharing api endpoint - * @param string $ticket - * The ticket, obtained by @getTicketForUser - * @return string - */ - public function getRESTAuthenticationHeader(string $ticket) { - return 'Authorization: EDU-TICKET ' . $ticket; - } - - - protected function getSignatureHeaders( - string $signString, - $accept = 'application/json', - $contentType = 'application/json' - ) { - $ts = time() * 1000; - $toSign = $this->base->appId . $signString . $ts; - $signature = $this->sign($toSign); - return [ - 'Accept: ' . $accept, - 'Content-Type: ' . $contentType, - 'X-Edu-App-Id: ' . $this->base->appId, - 'X-Edu-App-Signed: ' . $toSign, - 'X-Edu-App-Sig: ' . $signature, - 'X-Edu-App-Ts: ' . $ts, - ]; - } - - protected function sign(string $toSign) { - return $this->base->sign($toSign); - } -}
\ No newline at end of file diff --git a/vendor/edu-sharing-plugin/edu-sharing-helper-base.php b/vendor/edu-sharing-plugin/edu-sharing-helper-base.php deleted file mode 100644 index 4c6f6da..0000000 --- a/vendor/edu-sharing-plugin/edu-sharing-helper-base.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -class EduSharingHelperBase { - public $baseUrl; - public $privateKey; - public $appId; - public $language = 'de'; - public $http_proxy = ''; - - /** - * @param string $baseUrl - * The base url to your repository in the format "http://<host>/edu-sharing" - * @param string $privateKey - * Your app's private key. This must match the public key registered in the repo - * @param string $appId - * Your app id name (as registered in the edu-sharing repository) - */ - public function __construct( - string $baseUrl, - string $privateKey, - string $appId, - string $http_proxy = '' - ) { - if(!preg_match('/^([a-z]|[A-Z]|[0-9]|[-_])+$/', $appId)) { - throw new Exception('The given app id contains invalid characters or symbols'); - } - $this->baseUrl=$baseUrl; - $this->privateKey=$privateKey; - $this->appId=$appId; - $this->http_proxy = $http_proxy; - } - - public function setLanguage(string $language) { - $this->language = $language; - } - - function sign(string $toSign) { - $pkeyid = openssl_get_privatekey($this->privateKey); - openssl_sign($toSign, $signature, $pkeyid); - $signature = base64_encode($signature); - openssl_free_key($pkeyid); - return $signature; - } - -} diff --git a/vendor/edu-sharing-plugin/edu-sharing-helper.php b/vendor/edu-sharing-plugin/edu-sharing-helper.php deleted file mode 100644 index 3c9d884..0000000 --- a/vendor/edu-sharing-plugin/edu-sharing-helper.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -class EduSharingHelper { - /** - * generate a new key pair (private + public) to be registered in the edu-sharing repository - * Store the data somewhere in your application, e.g. database - * use the public key returned to register the application in edu-sharing - * NOTE: This function will fail on windows-based systems! - * @throws Exception - */ - public static function generateKeyPair( - ) - { - $res = openssl_pkey_new(); - if(!$res) { - throw new Exception("No result from openssl_pkey_new. Please check your php installation"); - } - openssl_pkey_export($res, $privatekey); - $publickey = openssl_pkey_get_details($res); - $publickey = $publickey["key"]; - return [ - "privatekey" => $privatekey, - "publickey" => $publickey - ]; - } - - /** - * Generates an edu-sharing compatible xml file for registering the application - * This is a very basic function and is only intended for demonstration or manual use. Data is not escaped! - */ - public static function generateEduAppXMLData(string $appId, string $publickey, string $type = 'LMS', string $publicIP = '*') { - return '<?xml version="1.0" encoding="UTF-8"?> - <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> - <properties> - <entry key="appid">' . $appId . '</entry> - <entry key="public_key">' . $publickey . '</entry> - <entry key="type">' . $type . '</entry> - <entry key="domain"></entry> - <!-- in case of wildcard host: Replace this, if possible, with the public ip from your service --> - <entry key ="host">' . $publicIP . '</entry> - <!-- must be true --> - <entry key="trustedclient">true</entry> - </properties> - '; - } - -}
\ No newline at end of file diff --git a/vendor/edu-sharing-plugin/edu-sharing-node-helper.php b/vendor/edu-sharing-plugin/edu-sharing-node-helper.php deleted file mode 100644 index 6c7a18e..0000000 --- a/vendor/edu-sharing-plugin/edu-sharing-node-helper.php +++ /dev/null @@ -1,184 +0,0 @@ -<?php -require_once "edu-sharing-helper-abstract.php"; - -class DisplayMode { - const Inline = 'inline'; - const Embed = 'embed'; - const Dynamic = 'dynamic'; -} -class Usage { - public $nodeId; - public $nodeVersion; - public $containerId; - public $resourceId; - public $usageId; - - public function __construct($nodeId, $nodeVersion, $containerId, $resourceId, $usageId) - { - $this->nodeId = $nodeId; - $this->nodeVersion = $nodeVersion; - $this->containerId = $containerId; - $this->resourceId = $resourceId; - $this->usageId = $usageId; - } - -} -class EduSharingNodeHelper extends EduSharingHelperAbstract { - /** - * creates a usage for a given node - * The given usage can later be used to fetch this node REGARDLESS of the actual user - * The usage gives permanent access to this node and acts similar to a license - * In order to be able to create an usage for a node, the current user (provided via the ticket) - * MUST have CC_PUBLISH permissions on the given node id - * @param string $ticket - * A ticket with the user session who is creating this usage - * @param string $containerId - * A unique page / course id this usage refers to inside your system (e.g. a database id of the page you include the usage) - * @param string $resourceId - * The individual resource id on the current page or course this object refers to - * (you may enumerate or use unique UUID's) - * @param string $nodeId - * The edu-sharing node id the usage shall be created for - * @param string|null $nodeVersion - * Optional: The fixed version this usage should refer to - * If you leave it empty, the usage will always refer to the latest version of the node - * @return Usage - * An usage element you can use with @getNodeByUsage - * Keep all data of this object stored inside your system! - */ - public function createUsage( - string $ticket, - string $containerId, - string $resourceId, - string $nodeId, - string $nodeVersion = null - ) { - $curl = curl_init($this->base->baseUrl . '/rest/usage/v1/usages/repository/-home-'); - $headers = $this->getSignatureHeaders($ticket); - $headers[] = $this->getRESTAuthenticationHeader($ticket); - curl_setopt_array($curl, [ - CURLOPT_FAILONERROR => false, - CURLOPT_POST => 1, - CURLOPT_POSTFIELDS => json_encode([ - 'appId' => $this->base->appId, - 'courseId' => $containerId, - 'resourceId' => $resourceId, - 'nodeId' => $nodeId, - 'nodeVersion' => $nodeVersion, - ]), - CURLOPT_RETURNTRANSFER => 1, - CURLOPT_HTTPHEADER => $headers - ]); - if ($this->base->http_proxy) { - curl_setopt($curl, CURLOPT_PROXY, $this->base->http_proxy); - } - $data = json_decode(curl_exec($curl), true); - $err = curl_errno( $curl ); - $info = curl_getinfo($curl); - curl_close($curl); - if ($err === 0 && $info["http_code"] === 200) { - return new Usage( - $data['parentNodeId'], - $nodeVersion, - $containerId, - $resourceId, - $data['nodeId'] - ); - } else { - throw new Exception('creating usage failed ' . - $info["http_code"] . ': ' . $data['error'] . ' ' . $data['message']); - } - - } - - /** - * Loads the edu-sharing node refered by a given usage - * @param Usage $usage - * The usage, as previously returned by @createUsage - * @param string $displayMode - * The displayMode - * This will ONLY change the content representation inside the "detailsSnippet" return value - * @param array $renderingParams - * @return mixed - * Returns an object containing a "detailsSnippet" repesentation - * as well as the full node as provided by the REST API - * Please refer to the edu-sharing REST documentation for more details - * @throws Exception - */ - public function getNodeByUsage( - Usage $usage, - $displayMode = DisplayMode::Inline, - array $renderingParams = null - ) - { - $url = $this->base->baseUrl . '/rest/rendering/v1/details/-home-/' . rawurlencode($usage->nodeId); - $url .= '?displayMode=' . rawurlencode($displayMode); - if($usage->nodeVersion) { - $url .= '&version=' . rawurlencode($usage->nodeVersion); - } - $curl = curl_init($url); - - $headers = $this->getSignatureHeaders($usage->usageId); - $headers[] = 'X-Edu-Usage-Node-Id: ' . $usage->nodeId; - $headers[] = 'X-Edu-Usage-Course-Id: ' . $usage->containerId; - $headers[] = 'X-Edu-Usage-Resource-Id: ' . $usage->resourceId; - - curl_setopt_array($curl, [ - CURLOPT_FAILONERROR => false, - CURLOPT_POST => 1, - CURLOPT_POSTFIELDS => json_encode($renderingParams), - CURLOPT_RETURNTRANSFER => 1, - CURLOPT_HTTPHEADER => $headers - ]); - if ($this->base->http_proxy) { - curl_setopt($curl, CURLOPT_PROXY, $this->base->http_proxy); - } - $data = json_decode(curl_exec($curl), true); - $err = curl_errno( $curl ); - $info = curl_getinfo($curl); - if ($err === 0 && $info["http_code"] === 200) { - return $data; - } else { - throw new Exception('fetching node by usage failed ' . - $info["http_code"] . ': ' . $data['error'] . ' ' . $data['message']); - } - } - - /** - * Deletes the given usage - * We trust that you've validated if the current user in your context is allowed to do so - * There is no restriction in deleting usages even from foreign users, as long as they were generated by your app - * Thus, this endpoint does not require any user ticket - * @param string $nodeId - * The edu-sharing node id this usage belongs to - * @param string $usageId - * The usage id - */ - public function deleteUsage( - string $nodeId, - string $usageId - ) { - $curl = curl_init($this->base->baseUrl . '/rest/usage/v1/usages/node/' . rawurlencode($nodeId) . '/' . rawurlencode($usageId)); - $headers = $this->getSignatureHeaders($nodeId.$usageId); - curl_setopt_array($curl, [ - CURLOPT_FAILONERROR => false, - CURLOPT_CUSTOMREQUEST => 'DELETE', - CURLOPT_RETURNTRANSFER => 1, - CURLOPT_HTTPHEADER => $headers - ]); - if ($this->base->http_proxy) { - curl_setopt($curl, CURLOPT_PROXY, $this->base->http_proxy); - } - $data = json_decode(curl_exec($curl), true); - $err = curl_errno( $curl ); - $info = curl_getinfo($curl); - curl_close($curl); - if ($err === 0 && $info["http_code"] === 200) { - - } else { - throw new Exception('deleting usage failed ' . - $info["http_code"] . ': ' . $data['error'] . ' ' . $data['message']); - } - - } -} diff --git a/vendor/edu-sharing-plugin/example/example-api.php b/vendor/edu-sharing-plugin/example/example-api.php deleted file mode 100644 index a961a7d..0000000 --- a/vendor/edu-sharing-plugin/example/example-api.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php -define('APP_ID', 'data-quest Test'); -define('BASE_URL', 'http://localhost:8080/edu-sharing'); -define('USERNAME', 'root@studip'); - - -header('Accept: application/json'); -header('Content-Type: application/json'); - -require_once "../edu-sharing-helper.php"; -require_once "../edu-sharing-helper-base.php"; -require_once "../edu-sharing-auth-helper.php"; -require_once "../edu-sharing-node-helper.php"; - -$privatekey = @file_get_contents('private.key'); -if(!$privatekey) { - die('no private key'); -} else { - $key["privatekey"] = $privatekey; -} -// init the base class instance we use for all helpers -$base = new EduSharingHelperBase(BASE_URL, $key["privatekey"], APP_ID); -$postData = json_decode(file_get_contents('php://input')); -$action = $postData->action; -$result = null; -if ($action === 'BASE_URL') { - $result = BASE_URL; -} else if ($action === 'GET_NODE') { - $nodeHelper = new EduSharingNodeHelper($base); - $result = $nodeHelper->getNodeByUsage( - new Usage( - $postData->nodeId, - $postData->nodeVersion, - $postData->containerId, - $postData->resourceId, - $postData->usageId - ) - ); -} else if ($action === 'CREATE_USAGE') { - $nodeHelper = new EduSharingNodeHelper($base); - $result = $nodeHelper->createUsage( - $postData->ticket, - $postData->containerId, - $postData->resourceId, - $postData->nodeId - ); -} else if ($action === 'DELETE_USAGE') { - $nodeHelper = new EduSharingNodeHelper($base); - $nodeHelper->deleteUsage( - $postData->nodeId, - $postData->usageId - ); -} else if ($action === 'TICKET') { - $authHelper = new EduSharingAuthHelper($base); - $ticket = $authHelper->getTicketForUser(USERNAME); - $result = $ticket; -} - -echo json_encode($result); diff --git a/vendor/edu-sharing-plugin/example/example.php b/vendor/edu-sharing-plugin/example/example.php deleted file mode 100644 index e959d59..0000000 --- a/vendor/edu-sharing-plugin/example/example.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -/** - * This is a sample file on how to use the edu-sharing remote library - * Run this script for the first time to create a private/public keypair - * On first run, a properties.xml file will be created - * Upload this file to your target edu-sharing (Admin-Tools -> Remote Systems -> Choose XML-File) - */ - -define('APP_ID', 'sample-app'); -define('USERNAME', 'tester'); -require_once "../edu-sharing-helper.php"; -require_once "../edu-sharing-helper-base.php"; -require_once "../edu-sharing-auth-helper.php"; -require_once "../edu-sharing-node-helper.php"; - -$privatekey = @file_get_contents('private.key'); -if(!$privatekey) { - $key = EduSharingHelper::generateKeyPair(); - // store the $key data inside your application, e.g. your database or plugin config - file_put_contents(APP_ID . '.properties.xml', EduSharingHelper::generateEduAppXMLData(APP_ID, $key['publickey'])); - file_put_contents('private.key', $key['privatekey']); - die('Wrote ' . APP_ID . '.properties.xml file. Upload it to edu-sharing, then run this script again'); -} else { - $key["privatekey"] = $privatekey; -} -if(count($argv) < 2) { - die('This script should be called as follow: "example.php http://localhost:8080/edu-sharing [<node-id>]"'); -} -// init the base class instance we use for all helpers -$base = new EduSharingHelperBase($argv[1], $key["privatekey"], APP_ID); -$base->setLanguage('de'); - -// authenticating (getting a ticket) and validating the given ticket -$authHelper = new EduSharingAuthHelper($base); -$ticket = $authHelper->getTicketForUser(USERNAME); -echo "Ticket validation result:\n"; -print_r($authHelper->getTicketAuthenticationInfo($ticket)); - -if(count($argv) !== 3) { - die("No node id given. Add a 3rd parameter to test create + fetching of nodes by usage"); -} -$nodeHelper = new EduSharingNodeHelper($base); -$usage = $nodeHelper->createUsage($ticket, '1', '1', $argv[2]); -echo "Usage create result:\n"; -print_r($usage); - -$node = $nodeHelper->getNodeByUsage($usage); -echo "Get node by usage:\n"; -print_r($node["node"]["name"]);
\ No newline at end of file diff --git a/vendor/edu-sharing-plugin/example/index.html b/vendor/edu-sharing-plugin/example/index.html deleted file mode 100644 index 9be18ce..0000000 --- a/vendor/edu-sharing-plugin/example/index.html +++ /dev/null @@ -1,120 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="UTF-8"> - <title>Edu Sharing Usage Example</title> - <style> - body > div { - padding: 20px 10px; - } - pre { - border: 1px solid #eee; - padding: 10px; - margin: 10px; - } - </style> - <script> - let ticket; - let baseUrl = null; - let esWindow = null; - function openEduSharing() { - esWindow = window.open( - baseUrl + '/components/search?ticket=' + encodeURIComponent(ticket) + - '&reurl=IFRAME' - ); - } - window.addEventListener('message', receiveMessage, false); - async function receiveMessage(event){ - if(event.data.event === 'APPLY_NODE'){ // Event Name hier festlegen - esWindow.close(); - console.log(event.data.data); - usage = await createUsage(event.data.data.nodeId); - // in a real application, the usage is stored in your backend system - localStorage.setItem('usage', JSON.stringify(usage)); - await renderUsage(); - - } - } - async function renderUsage() { - if(localStorage.getItem('usage')) { - const usage = JSON.parse(localStorage.getItem('usage')); - document.querySelector('#usage').style.display = null; - document.querySelector('#delete-usage').style.display = null; - document.querySelector('#usage').innerHTML = JSON.stringify(usage, null, 4); - usage.action = 'GET_NODE'; - const render = await fetchAPI(usage) - document.querySelector('#render').innerHTML = render.detailsSnippet; - } - - } - async function createUsage(nodeId) { - return await fetchAPI({ - action: 'CREATE_USAGE', - ticket, - nodeId, - containerId: 'my_sample_page_1', - resourceId: Math.random() - }); - } - async function deleteUsage(nodeId) { - const usage = JSON.parse(localStorage.getItem('usage')); - await fetchAPI({ - action: 'DELETE_USAGE', - nodeId: usage.nodeId, - usageId: usage.usageId - }); - localStorage.removeItem('usage'); - document.querySelector('#render').style.display = 'none'; - document.querySelector('#usage').style.display = 'none'; - document.querySelector('#delete-usage').style.display = 'none'; - } - async function fetchAPI(data) { - return new Promise((resolve, reject) => { - var xhr = new XMLHttpRequest(); - xhr.open("POST", "example-api.php", true); - xhr.onload = () => { - if (xhr.readyState === 4) { - if (xhr.status === 200) { - resolve(JSON.parse(xhr.response)); - } else { - alert(xhr.statusText); - reject(xhr.statusText); - } - } - }; - xhr.onerror = function (e) { - alert(xhr.statusText); - }; - xhr.send(JSON.stringify(data)); - }); - } - async function getTicket() { - ticket = await fetchAPI({action: 'TICKET'}); - document.querySelector('#ticket').innerText = ticket; - document.querySelector('#edu-select').style.display = null; - - } - window.addEventListener('load', async () => { - baseUrl = await fetchAPI({action: 'BASE_URL'}); - await renderUsage(); - - await getTicket(); - }); - </script> -</head> -<body> -<div> - <button onclick="getTicket()">Re-Fetch ticket</button> - <span id="ticket">No ticket</span> -</div> -<div id="edu-select" style="display:none;"> - <button onclick="openEduSharing()">Open edu-sharing & select node</button> -</div> -<pre id="usage" style="display: none"></pre> -<div id="delete-usage" style="display: none"> - <button onclick="deleteUsage()">Delete current Usage</button> -</div> -<div id="render"></div> - -</body> -</html>
\ No newline at end of file diff --git a/vendor/edu-sharing-plugin/readme.md b/vendor/edu-sharing-plugin/readme.md deleted file mode 100644 index ab1f332..0000000 --- a/vendor/edu-sharing-plugin/readme.md +++ /dev/null @@ -1 +0,0 @@ -This library is a changed version of https://github.com/edu-sharing/php-auth-plugin |
