Vous avez reçu un message "Your GitLab account has been locked ..." ? Pas d'inquiétude : lisez cet article https://docs.gricad-pages.univ-grenoble-alpes.fr/help/unlock/

Commit 933b7f32 authored by Francois Gannaz's avatar Francois Gannaz
Browse files

rewrite for labnbook/laravel

parent 770cef32
/nbproject/
/vendor/
This diff is collapsed.
This diff is collapsed.
<?php
/**
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU GPL v3
*/
namespace mod_labnbook\ar;
/* @var $DB \moodle_database */
/**
* AR class that map the "labnbook" table in the DB.
*/
class labnbook
{
public $id;
public $courseid;
public $name;
public $intro;
public $introformat;
public $labnbook_missionid;
public $labnbook_teamconfigid;
public function __construct(array $record = [])
{
foreach ($record as $field => $value) {
if (object_property_exists($this, $field)) {
$this->$field = $value;
}
}
}
public static function find(array $criteria)
{
global $DB;
$record = $DB->get_record(LABNBOOK_TABLE, $criteria, '*');
return new self($record);
}
}
<?php
/**
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU GPL v3
*/
namespace mod_labnbook\ar;
/* @var $DB \moodle_database */
/**
* AR class that map the "labnbook_teamconfig" table in the DB.
*/
class teamconfig
{
const METHOD_STUDENTCHOICE = 1;
const METHOD_TEACHERCHOICE = 2;
const METHOD_RANDOM = 3;
public $id;
public $instanceid;
public $groupid;
public $method;
public $sizeopt;
public $sizemin;
public $sizemax;
public $teamsmax;
public $starttime;
public $endtime;
public $sendteacher;
public $sendteam;
public $sendclass;
public $sendld;
public $sendmission;
public $saveld;
public $importld;
public $nameprefix;
public $timemodified;
public function __construct(array $record = [])
{
foreach (['id', 'instanceid', 'groupid', 'method', 'sizeopt', 'sizemin', 'sizemax', 'teamsmax', 'importld', 'starttime', 'endtime', 'timemodified'] as $field) {
if (empty($record[$field])) {
$this->$field = null;
} else {
$this->$field = (int) $record[$field];
}
}
foreach (['sendteacher', 'sendteam', 'sendclass', 'sendld', 'senmission', 'saveld'] as $field) {
if (empty($record[$field])) {
$this->$field = true;
} else {
$this->$field = (boolean) $record[$field];
}
}
$this->nameprefix = empty($record['nameprefix']) ? '' : (string) $record['nameprefix'];
}
public static function find(array $criteria)
{
global $DB;
$record = $DB->get_record("labnbook_teamconfig", $criteria, '*');
return new self($record);
}
public static function findForGroups($instanceid, array $groupids)
{
global $DB;
$sql = "SELECT * "
. "FROM {labnbook_teamconfig} tc"
. " WHERE tc.instanceid = ?";
$params = [(int) $instanceid];
if ($groupids) {
$sql .= " AND (groupID IN (" . join(",", array_fill(0, count($groupids) - 1, "?")) . ") OR groupid IS NULL)";
foreach ($groupids as $id) {
$params[] = $id;
}
$sql .= " ORDER BY (groupid IS NULL) ASC";
} else {
$sql .= " AND groupid IS NULL";
}
$sql .= " LIMIT 1";
$record = $DB->get_record_sql($sql, $params);
if ($record) {
return new teamconfig((array) $record);
} else {
return null;
}
}
}
<?php
/**
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU GPL v3
*/
namespace mod_labnbook\fetch;
class environment
{
/**
* @var string "inst"|"user"
*/
public $orig;
/**
* @var int issuer (institution ID)
*/
public $iss;
/**
* @var int subject (user ID)
*/
public $sub;
/**
* @var string Expiration timestamp, as received from the server
*/
public $token_exp;
public function createPayload() : array {
$payload = [
'iss' => $this->iss,
'sub' => $this->sub,
'orig' => $this->orig,
];
if ($this->token_exp) {
$payload['token_exp'] = $this->token_exp;
}
return $payload;
}
}
<?php
/**
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU GPL v3
*/
namespace mod_labnbook\fetch;
require_once dirname(dirname(__DIR__)) . '/vendor/firebase/php-jwt/src/JWT.php';
use \Firebase\JWT\JWT;
/**
* Fetch info from the LabNbook API for the current user.
*/
class fetcher
{
/**
* @var string Root URL to the API
*/
private $baseUrl;
/**
* @var string Secret key for signing JWT
*/
private $secret;
/**
* @var environment base of the JWT payload across requests
*/
private $environment;
/**
* @var array { code, message }, e.g. { code: 403, message: "not auth" }
*/
private $lastError;
public function __construct(string $baseUrl, string $secret, environment $environment) {
if (!$secret) {
throw new \Exception("The JWT secret key is empty.");
}
$this->baseUrl = rtrim($baseUrl, '/');
$this->secret = $secret;
$this->environment = $environment;
}
/**
* @return \Stdclass {code, message}
*/
public function getLastError() : ?\Stdclass {
return $this->lastError;
}
/**
* Query the LabNbook API.
*
* @param string $verb E.g. "GET"
* @param string $urlPath E.g. "/v1/mission"
* @param array $payload Default is empty. Else any assoc array.
* @return mixed
*/
public function fetch(string $verb, string $urlPath, array $payload = []) {
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $verb,
CURLOPT_URL => $this->createUrl($urlPath),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Accept: application/json',
'Authorization: Bearer ' . $this->encodeJwtToken($urlPath, $payload),
],
]);
$response = curl_exec($curl);
$error = $this->validateResponse($curl, $response);
if ($error) {
curl_close($curl);
error_log("Query $verb $urlPath to labnbook API failed. $error");
throw new \Exception("HTTP request failed.");
}
// send decoded response?
$contentType = curl_getinfo($curl, CURLINFO_CONTENT_TYPE);
curl_close($curl);
if ($contentType === 'application/json') {
return json_decode($response);
} else {
return $response;
}
}
public function createUrl(string $urlPath) : string {
return $this->baseUrl . '/' . ltrim($urlPath, '/');
}
/**
* Returns the JS source code that will fetch the data in a promise.
*
* @param string $verb E.g. "GET"
* @param string $urlPath E.g. "/v1/mission"
* @param array $payload Default is empty. Else any assoc array.
* @return string JS source code
*/
public function getJsFetch(string $verb, string $urlPath, array $payload = []) {
$url = \json_encode($this->createUrl($urlPath));
$verb = \json_encode($verb);
$token = $this->encodeJwtToken($urlPath, $payload);
// no cache, no cookies, no referrer
return <<<EOJS
fetch($url, {
method: $verb,
cache: 'no-cache',
credentials: 'omit',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer $token',
},
mode: 'cors',
referrer: 'no-referrer',
})
EOJS;
}
public function encodeJwtToken(string $urlPath, array $payload) : string {
$jwtPayload = $this->environment->createPayload();
if ($payload) {
$jwtPayload['data'] = $payload;
}
$jwtPayload['dest'] = '/' . ltrim($urlPath, '/');;
$jwtPayload['iat'] = time(); // UTC timestamp
return JWT::encode($jwtPayload, $this->secret, 'HS256');
}
/**
* @param resource $curl
* @param resource $response
* @return string if OK "", else error message
*/
protected function validateResponse($curl, $response) {
if ($response === false) {
$this->lastError = ['message' => "Network error? " . curl_error($curl)];
return "Network error? " . curl_error($curl);
}
$responseCode = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
if ($responseCode != 200) {
$this->lastError = json_decode($response); // Do not verify signature?
if (json_last_error() !== JSON_ERROR_NONE) {
$this->lastError = (object) ['message' => $response];
}
$this->lastError->code = $responseCode;
return "HTTP code $responseCode, response: " . $response;
}
return "";
}
}
<?php
/**
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU GPL v3
*/
namespace mod_labnbook\fetch;
class institution extends moodlefetcher
{
const OK = 200;
const ERROR_NETWORK = 0;
const ERROR_TOKEN_NOTVALID = 401;
const ERROR_LOGIN_UNKNOWN = 403;
const ERROR_CREATION_FAILED = 500;
protected function getSigningKey() : string {
global $CFG;
return $CFG->labnbook_api_key;
}
/**
* @return int Cf constants: OK, ERROR_*
*/
public function loginUser() : int {
try {
$response = $this->fetcher->fetch("POST", "/v1/auth/login");
} catch (\Exception $e) {
$error = $this->fetcher->getLastError();
return (isset($error->code) ? (int) $error->code : 0);
/*
if ($error->code === self::ERROR_LOGIN_UNKNOWN) {
// TODO
} else if ($error->code === self::ERROR_TOKEN_NOTVALID) {
// TODO
} else {
// TODO
}
*/
}
return self::OK;
}
public function createUser($user) {
try {
$response = $this->fetcher->fetch("PUT", "/v1/user/create", ['data' => $user]);
} catch (\Exception $e) {
$error = $this->fetcher->getLastError();
if ($error->code === self::ERROR_CREATION_FAILED) {
// TODO
} else if ($error->code === self::ERROR_TOKEN_NOTVALID) {
// TODO
} else {
// TODO
}
return $error->code;
}
// creation successful
// TODO
return self::OK;
}
}
<?php
/**
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU GPL v3
*/
namespace mod_labnbook\fetch;
abstract class moodlefetcher
{
/**
* @var fetcher
*/
protected $fetcher;
public function __construct() {
global $CFG;
$this->fetcher = new fetcher(
$CFG->labnbook_api_url,
$this->getSigningKey(),
$this->getEnvironment()
);
}
protected function getEnvironment() : environment {
global $CFG, $USER;
$env = new environment();
$env->orig = "inst";
$env->iss = (int) $CFG->labnbook_institution_id;
$env->sub = (int) $USER->id;
return $env;
}
protected function fetch($verb, $path, $payload) {
global $SESSION;
$response = $this->fetcher->fetch($verb, $path, $payload);
if (!empty($response->auth)) {
if (empty($SESSION->labnbook)) {
$SESSION->labnbook = [];
}
$SESSION->labnbook['auth'] = $response->auth;
}
return isset($response->data) ? $response->data : $response;
}
abstract protected function getSigningKey() : string;
}
<?php
/**
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU GPL v3
*/
namespace mod_labnbook\fetch;
class user extends moodlefetcher
{
const ERROR_TOKEN_NOTVALID = 401;
const ERROR_LOGIN_UNKNOWN = 403;
const ERROR_CREATION_FAILED = 500;
public static function isAuthenticated() {
global $SESSION;
return !empty($SESSION->labnbook['auth']['token_exp']);
}
protected function getEnvironment() : environment {
global $SESSION;
if (empty($SESSION->labnbook['auth']['token_exp'])) {
throw new \Exception("User token for the Labnbook API does not exist.");
}
$env = parent::getEnvironment();
$env->token_exp = $SESSION->labnbook['auth']['token_exp'];
return $env;
}
protected function getSigningKey() : string {
global $SESSION;
if (empty($SESSION->labnbook['auth']['token'])) {
throw new \Exception("User token for the Labnbook API does not exist.");
}
return $SESSION->labnbook['auth']['token'];
}
/**
* @return array mission[]
*/
public function getMissions() {
return $this->fetch('GET', '/mission')->data;
}
/**
* Return the JS code that fetches the missions.
*
* E.g. <?= $u->getJsMissions() ?>.then(missions => for (m of missions) {...})
*
* @return string JavaScript Promise
*/
public function getJsMissions() {
return $this->fetcher->getJsFetch($verb, $path, $payload)
. '.then(x => x.data)';
}
}
<?php
/**
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU GPL v3
*/
/* @var $DB \moodle_database */
namespace mod_labnbook\session;
/**
* Queries on LabNbook info for the current user.
*/
class labnbook
{
private static $singleton;
/**
* @var string
*/
private $url;
/**
* @var string
*/
private $secret;
/**
* @var int
*/
private $userId;
/**
* @var array
*/
private $missions;
private $identifyByCasAuth = true;
private $identifyByStudentNumber = true;
private $autoCreateUser = true;
/**
* @return \\mod_labnbook\session\labnbook
*/
public static function load() {
if (empty(self::$singleton)) {
self::$singleton = new self;
}
return self::$singleton;
}
protected function __construct() {
global $CFG, $USER;
$this->url = rtrim($CFG->labnbook_url, '/');
$this->secret = $CFG->labnbook_key;
$this->userId = $this->fetchUserId($USER->id);
}
/**
* Empties the cache and session, so forces to fetch data from the LabNbook server.
*
* $refreshedUserId = $session->refresh()->getUserId();
*
* @return \mod_labnbook\session\labnbook
*/
public function refresh() {
global $SESSION;
unset($this->userId);
unset($SESSION->labnbook_userid);
unset($this->missions);
unset($SESSION->labnbook_missions);
return $this;
}
/**
* Search the LabNbook user ID in the cache (object attribute),
* then in the user session,
* then the DB,
* then through the LabNbook REST API (first GET, then PUT).
*
* @return int
*/
public function getUserId() {
if (!isset($this->userId)) {
$this->userId = $this->fetchUserId();
}
return $this->userId;
}
/**
* @return array mission[]
*/
public function getMissions($refresh = false) {
if ($refresh || !isset($this->missions)) {
$this->missions = $this->fetchMissions($refresh);
}
return $this->missions;
}
/**
* @global moodle_database $DB
*
* @param int $instanceId
* @return int|null
*/
public function instanceIdToMissionId($instanceId) {
global $DB;
return $DB->get_field("labnbook", "labnbook_missionid", ['id' => $instanceId]);
}
/**
* @param int $instanceId