diff --git a/.gitignore b/.gitignore index e5c3a50..31c7287 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ ## ignore our config file. See config.php.sample for a blank includes/config.php +includes/errorlog.txt .gitignore~ .htaccess~ quizzes/ + +.idea diff --git a/README.md b/README.md index 0f9e042..e7b65cd 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,24 @@ ## A quiz builder and formative assessment tool for legal education CALI QuizWright is a tool for creating and publishing formative assessments for your class. QuizWright gives law teachers a web-based platform to create and manage personal question banks and quizzes for their students. When used with CALI's AutoPublish, LessonLive, and LessonLink features, QuizWright quizzes can be delivered to students and taken in real time in the classroom using the familiar CALI Lesson Viewer. + +## simpleSAMLphp service provider + +CALI Author uses simpleSAMLphp as its SAML service provider and authenticates using the CALI website as the SAML identity provider. + +This integration expects QuizWright to be installed in a folder named `quizwright` in the project root folder (not `web/`!) of the `cali-drupal` codebase. The Apache virtual host for Drupal should include the following to make `quizwright` available from this location: + +```apacheconf +# QuizWright configuration +Alias /quizwright /path/to/drupal/quizwright + + + Options FollowSymLinks + AllowOverride None + Require all granted + +``` + +All of simpleSAMLphp's custom configurations for the CALI-Author SAML SP are in the `cali-drupal` codebase. + +For QuizWright to use the service provider, the `USER_MGMNT` constant in `includes/config.php` should point to `includes/saml_user.php`. diff --git a/includes/config.php.default b/includes/config.php.default index 7e7959b..3b877df 100644 --- a/includes/config.php.default +++ b/includes/config.php.default @@ -4,29 +4,28 @@ */ // db options -define('DB_NAME', 'your db name'); -define('DB_USER', 'your db username'); -define('DB_PASSWORD', 'your db password'); -define('DB_HOST', 'localhost'); +const DB_NAME = 'quizwright'; +const DB_USER = 'db'; +const DB_PASSWORD = 'db'; +const DB_HOST = 'db'; $mysqli = new mysqli(DB_HOST,DB_USER,DB_PASSWORD,DB_NAME); // full path to user management -define('USER_MGMNT', '/local/var/www/caw/local_user.php'); -define('DRUPAL_REGISTER', 'http://your.drupal/regsiter/page'); +const USER_MGMNT = '/var/www/html/quizwright/includes/saml_user.php'; +const DRUPAL_REGISTER = 'http://your.drupal/regsiter/page'; // define connection info for user info source. // could be another db or something like LDAP -define('UDB_NAME', 'user db'); -define('UDB_USER', 'user db username'); -define('UDB_PASSWORD', 'user db password'); -define('UDB_HOST', 'localhost'); +const UDB_NAME = 'user db'; +const UDB_USER = 'user db username'; +const UDB_PASSWORD = 'user db password'; +const UDB_HOST = 'localhost'; -$umysqli = new mysqli(UDB_HOST,UDB_USER,UDB_PASSWORD,UDB_NAME); +//$umysqli = new mysqli(UDB_HOST,UDB_USER,UDB_PASSWORD,UDB_NAME); //full URL of your site -define('SITE_URL', 'http://where.is.my.site/') +const SITE_URL = https://cali-d10.ddev.site/quizwright/'; //where to publish quizzes -define('PUBLISH_URL','http://where.is.my.publish.endpoint/') -?> \ No newline at end of file +const PUBLISH_URL = 'https://cali-d10.ddev.site/publisher/publish/qw'; diff --git a/includes/home.php b/includes/home.php index 3915ba2..b082bc9 100755 --- a/includes/home.php +++ b/includes/home.php @@ -17,7 +17,7 @@
  • My Profile
  • -
  • Logout
  • +
  • Logout
  • diff --git a/includes/page-update.php b/includes/page-update.php index 215ed1d..ef1ab7e 100755 --- a/includes/page-update.php +++ b/includes/page-update.php @@ -6,8 +6,8 @@ //error_reporting(E_ALL); require ("user-session.php"); require ("utility.php"); -$pid = $_POST['pid']; -if (!isset($pid)) +$pid = $_POST['pid'] ?? null; +if (empty($pid)) { // No ID? Must be a brand new page. if ($_POST['page-type'] != '') { diff --git a/includes/profile.php b/includes/profile.php index 08e4c6d..a63011b 100755 --- a/includes/profile.php +++ b/includes/profile.php @@ -7,7 +7,7 @@ $sql = "SELECT profile FROM `people` WHERE uid = '$uid'"; if ($result = $mysqli->query($sql)) { while ($row = $result->fetch_assoc()) { - $data = json_decode($row['profile'], TRUE); + $data = json_decode($row['profile'] ?? '{}', TRUE) ?: []; ?> @@ -28,8 +28,8 @@
    - - + +
    @@ -37,8 +37,8 @@
    - - + +
    @@ -46,8 +46,8 @@
    - - + +
    @@ -55,8 +55,8 @@
    - - + +
    @@ -64,8 +64,8 @@
    - - + +
    diff --git a/includes/quiz-create.php b/includes/quiz-create.php index c0f8b93..3f8d625 100755 --- a/includes/quiz-create.php +++ b/includes/quiz-create.php @@ -7,8 +7,8 @@ $data = json_encode($_POST); // Old, bad quotes: $mysqli->query("INSERT INTO info (lid,uid,data) VALUES ('',$uid,'$data')"); -$stmt = mysqli_prepare($mysqli, "INSERT INTO info (lid,uid,data) VALUES (?,?,?)"); -mysqli_stmt_bind_param($stmt, "iis", $lid, $uid, $data); +$stmt = mysqli_prepare($mysqli, "INSERT INTO info (uid,data) VALUES (?,?)"); +mysqli_stmt_bind_param($stmt, "is", $uid, $data); mysqli_stmt_execute($stmt); $result=mysqli_connect_error(); $lid = $mysqli->insert_id; diff --git a/includes/saml_user.php b/includes/saml_user.php new file mode 100644 index 0000000..d5919b6 --- /dev/null +++ b/includes/saml_user.php @@ -0,0 +1,115 @@ +isAuthenticated()) { + return "User not authenticated via SAML."; + } + + $attributes = $as->getAttributes(); + + // Use 'cn' (common name) as the username, fallback to 'uid' if not available + $name = $attributes['cn'][0] ?? $attributes['uid'][0] ?? $attributes['username'][0] ?? null; + $email = $attributes['mail'][0] ?? $attributes['email'][0] ?? null; + $roles = $attributes['roles'] ?? []; + + // Get the Drupal UID for reference + $drupal_uid = $attributes['uid'][0] ?? null; + + if (!$name) { + return "Missing username from SAML assertion."; + } + + // Check for required role - QuizWright requires CALI Staff or facstaff role + $hasRequiredRole = false; + $CALIStaff = false; + $requiredRoles = ['cali_staff', 'facstaff']; + + foreach ($roles as $role) { + if (in_array($role, $requiredRoles)) { + $hasRequiredRole = true; + // Check if user is CALI Staff + if ($role === 'cali_staff') { + $CALIStaff = true; + } + break; + } + } + + if (!$hasRequiredRole) { + + return "User does not have required role. QuizWright requires a CALI member faculty/staff account."; + } + + $stmt = $mysqli->prepare("SELECT * FROM `people` WHERE username = ?"); + $stmt->bind_param("s", $name); + $stmt->execute(); + $result = $stmt->get_result(); + $count = $result->num_rows; + + if ($count == 1) { + $row = $result->fetch_array(MYSQLI_ASSOC); + $_SESSION['username'] = $row['username']; + $_SESSION['uid'] = $row['uid']; + $_SESSION['CALIStaff'] = $CALIStaff; + $err = ""; + } else { + $data = json_encode($attributes); + // SAML users don't have passwords - use empty string + $password = ''; + $stmt = $mysqli->prepare("INSERT INTO `people` (username, email, password, data) VALUES (?, ?, ?, ?)"); + $stmt->bind_param("ssss", $name, $email, $password, $data); + + if ($stmt->execute()) { + $uid = $mysqli->insert_id; + $_SESSION['username'] = $name; + $_SESSION['uid'] = $uid; + $_SESSION['CALIStaff'] = $CALIStaff; + $err = ""; + } else { + $err = sprintf("Error: %s\n", $mysqli->error); + } + } + + return $err; +} + +function userLogoutViaSAML(): void +{ + // Local logout only - does NOT log out of IdP (Drupal) + // Just destroy the local PHP session + session_unset(); + session_destroy(); + + // Clear SimpleSAMLphp auth data from session without triggering SAML logout + if (isset($_COOKIE['SimpleSAMLAuthToken'])) { + setcookie('SimpleSAMLAuthToken', '', time() - 3600, '/', '', false, true); + } + + // Redirect back to the app login page + header('Location: ' . (SITE_URL ?? '/quizwright/')); + exit; +} diff --git a/includes/user-session.php b/includes/user-session.php index aff3e3c..eaaa7a8 100755 --- a/includes/user-session.php +++ b/includes/user-session.php @@ -1,5 +1,7 @@ diff --git a/index.php b/index.php index 18d3d7a..2ddf6cb 100755 --- a/index.php +++ b/index.php @@ -1,7 +1,57 @@ 0; + +if (!$isAuthenticated) { + // Show login page instead of the app + ?> + + + + + + CALI QuizWright - Login Required + + + + +
    +
    +

    CALI QuizWright

    +

    QuizWright is a web app that lets teachers write individual MC, T/F, Y/N questions, + saves the questions in a personal question bank, allows teachers to bundle the questions + into quizzes, turns the quizzes into AutoPublish Lessons that are published to the CALI + website and run by students either as LessonLive or LessonLink assessments.

    +

    All you need to use QuizWright is a CALI member faculty/staff account.

    + + Log in with your CALI Account + +
    +
    + + + - +