Source for file sloodle_session.php

Documentation is available at sloodle_session.php

  1. <?php
  2.     // This file is part of the Sloodle project (www.sloodle.org)
  3.     
  4.     /**
  5.     * This file defines the primary API class, SloodleSession.
  6.     *
  7.     * @package sloodle
  8.     * @copyright Copyright (c) 2008 Sloodle (various contributors)
  9.     * @license http://www.gnu.org/licenses/gpl-3.0.html GNU GPL v3
  10.     *
  11.     * @contributor Peter R. Bloomfield
  12.     */
  13.     
  14.     /** General functionality. */
  15.     require_once(SLOODLE_LIBROOT.'/general.php');
  16.     /** Request and response functionality. */
  17.     require_once(SLOODLE_LIBROOT.'/io.php');
  18.     /** User functionality. */
  19.     require_once(SLOODLE_LIBROOT.'/user.php');
  20.     /** Course functionality. */
  21.     require_once(SLOODLE_LIBROOT.'/course.php');
  22.     /** Sloodle Controller functionality. */
  23.     require_once(SLOODLE_LIBROOT.'/controller.php');
  24.     /** Module functionality. */
  25.     require_once(SLOODLE_LIBROOT.'/modules.php');
  26.     
  27.     
  28.     /**
  29.     * The primary API class, which manages all other parts.
  30.     * @package sloodle
  31.     */
  32.     class SloodleSession
  33.     {
  34.     // DATA //
  35.         
  36.         /**
  37.         * Incoming HTTP request.
  38.         * @var SloodleRequest 
  39.         * @access public
  40.         */
  41.         var $request = null;
  42.     
  43.         /**
  44.         * Outgoing response - can be rendered to HTTP or as a string.
  45.         * @var SloodleResponse 
  46.         * @access public
  47.         */
  48.         var $response = null;
  49.         
  50.         /**
  51.         * Current user information.
  52.         * @var SloodleUser 
  53.         * @access public
  54.         */
  55.         var $user = null;
  56.         
  57.         /**
  58.         * The Sloodle course structure for the course this session is accessing
  59.         * @var SloodleCourse 
  60.         * @access public
  61.         */
  62.         var $course = null;
  63.         
  64.         /**
  65.         * The Sloodle module this session relates to, if any.
  66.         * Note: this may be the base Sloodle module class, or a derivative.
  67.         * @var SloodleModule 
  68.         * @access public
  69.         */
  70.         var $module = null;
  71.         
  72.         
  73.     // FUNCTIONS //
  74.     
  75.         /**
  76.         * Constructor - initialises members
  77.         * @param bool $process If true (default) then basic request data will be processed immediately. Otherwise, it can be done manually by calling $request->process_request_data()
  78.         */
  79.         function SloodleSession($process true)
  80.         {
  81.             // Construct the different parts of the session, as far as possible
  82.             $this->user = new SloodleUser($this);
  83.             $this->response = new SloodleResponse();
  84.             $this->request = new SloodleRequest($this);
  85.             $this->course = new SloodleCourse();
  86.             
  87.             // Process the basic request data
  88.             if ($process$this->request->process_request_data();
  89.         }
  90.         
  91.         
  92.         /**
  93.         * Constructs and loads the appropriate module part of the session.
  94.         * Note that this function will fail if the current VLE user (in the $user member) does not have permission to access it.
  95.         * @param string $type The expected type of module - function fails if type is not correctly matched
  96.         * @param bool $db If true then the system will also try to load appropriate data from the database, as specified in the module ID request parameter
  97.         * @param bool $require If true, then if something goes wrong, the script will be terminated with an error message
  98.         * @param bool $override_access If true, then the user access permissions will be overriden to force access (not implemented yet)
  99.         * @return bool True if successful, or false otherwise. (Note, if parameter $require was true, then the script will terminate before this function returns if something goes wrong)
  100.         */
  101.         function load_module($type$db$require true$override_access false)
  102.         {
  103.             // If the database loading is requested, then make sure we have a parameter to load with
  104.             $db_id null;
  105.             if ($db{
  106.                 $db_id $this->request->get_module_id($require);
  107.                 if ($db_id == nullreturn false;
  108.             }
  109.             
  110.             // Construct the module
  111.             $this->module = sloodle_load_module($type$this$db_id);
  112.             if (!$this->module{
  113.                 if ($require{
  114.                     $this->response->quick_output(-601'MODULE''Failed to construct module object'false);
  115.                     exit();
  116.                 }
  117.                 return false;
  118.             }
  119.             
  120.             return true;
  121.         }
  122.         
  123.         
  124.         /**
  125.         * Verifies security for the incoming request (but does not check user access).
  126.         * Initially ensures that the request is coming in on a valid and enabled course/controller (rejects it if not).
  127.         * The password is then checked, and it can handle prim-passwords and object-specific passwords.
  128.         *
  129.         * @param bool $require If true, the function will NOT return on authentication failure. Rather, it will terminate the script with an error message.
  130.         * @return bool true if successful in authenticating the request, or false if not.
  131.         */
  132.         function authenticate_request$require true )
  133.         {
  134.             // Make sure that the request data has been processed
  135.             if (!$this->request->is_request_data_processed()) {
  136.                 $this->request->process_request_data();
  137.             }
  138.             
  139.             // Make sure the controller ID parameter was specified
  140.             if ($this->request->get_controller_id($require=== nullreturn false;
  141.             
  142.             // Make sure we've got a valid course and controller object
  143.             if (!$this->course->controller->is_loaded()) {
  144.                 if ($require{
  145.                     $this->response->quick_output(-514'COURSE''Course controller could not be accessed.'false);
  146.                     exit();
  147.                 }
  148.                 return false;
  149.             }
  150.             if (!$this->course->is_loaded()) {
  151.                 if ($require{
  152.                     $this->response->quick_output(-512'COURSE''Course could not be accessed.'false);
  153.                     exit();
  154.                 }
  155.                 return false;
  156.             }
  157.             
  158.             // Make sure the course is available
  159.             if (!$this->course->is_available()) {
  160.                 if ($require{
  161.                     $this->response->quick_output(-513'COURSE''Course not available.'false);
  162.                     exit();
  163.                 }
  164.                 return false;
  165.             }
  166.             // Make sure the contrller is available
  167.             if (!$this->course->controller->is_available()) {
  168.                 if ($require{
  169.                     $this->response->quick_output(-514'COURSE''Course controller not available.'false);
  170.                     exit();
  171.                 }
  172.                 return false;
  173.             }
  174.             
  175.             // Make sure the controller is enabled
  176.             if (!$this->course->controller->is_enabled()) {
  177.                 if ($require{
  178.                     $this->response->quick_output(-514'COURSE''Course controller disabled.'false);
  179.                     exit();
  180.                 }
  181.                 return false;
  182.             }
  183.         
  184.             // Get the password parameter
  185.             $password $this->request->get_password($require);
  186.             if ($password == null{
  187.                 if ($require{
  188.                     $this->response->quick_output(-212'OBJECT_AUTH''Prim Password cannot be empty.'false);
  189.                     exit();
  190.                 }
  191.                 return false;
  192.             }
  193.             
  194.             // Does the password contain an object UUID?
  195.             $parts explode('|'$password);
  196.             if (count($parts>= 2{
  197.                 $objuuid $parts[0];
  198.                 $objpwd $parts[1];
  199.                 // Make sure the password was provided
  200.                 if (empty($objpwd)) {
  201.                     if ($require{
  202.                         $this->response->quick_output(-212'OBJECT_AUTH''Object-specific password not specified.'false);
  203.                         exit();
  204.                     }
  205.                     return false;
  206.                 }
  207.                 
  208.                 // Verify the object's authorisation
  209.                 if ($this->course->controller->check_authorisation($objuuid$objpwd)) {
  210.                     // Passed authorisation - make sure the object is registered as being still active
  211.                     $this->course->controller->ping_object($objuuid);
  212.                     return true;
  213.                 }
  214.                 if ($require{
  215.                     $this->response->quick_output(-213'OBJECT_AUTH''Object-specific password was invalid.'false);
  216.                     exit();
  217.                 }
  218.                 return false;
  219.             }
  220.             
  221.             // Get the controller password
  222.             $controllerpwd $this->course->controller->get_password();
  223.             // Prim Password access is disabled if no password has been specified
  224.             if (strlen($controllerpwd== 0{
  225.                 if ($require{
  226.                     $this->response->quick_output(-213'OBJECT_AUTH''Access to this Controller by prim password has been disabled.'false);
  227.                     exit();
  228.                 }
  229.                 return false;
  230.             }
  231.             // Check that the passwords match
  232.             if ($password != $this->course->controller->get_password()) {
  233.                 if ($require{
  234.                     $this->response->quick_output(-213'OBJECT_AUTH''Prim password was invalid.'false);
  235.                     exit();
  236.                 }
  237.                 return false;
  238.             }
  239.  
  240.             return true;
  241.         }
  242.         
  243.         
  244.         /**
  245.         * Verifies security for the incoming user-centric request.
  246.         * This ensures that the identified object is authorised for user-centric activities with the specified user.
  247.         * @param bool $require If TRUE (default) then the script will terminate with an error message on failure. Otherwise, it will return false on failure.
  248.         * @return bool TRUE if successful, or FALSE on failure (unless parameter $require was TRUE).
  249.         */
  250.         function authenticate_user_request$require true )
  251.         {
  252.             // Get the avatar UUID parameter
  253.             $avuuid $this->request->get_avatar_uuid($require);
  254.             if ($avuuid == null{
  255.                 if ($require{
  256.                     $this->response->quick_output(-212'OBJECT_AUTH''Avatar UUID required for user-centric request authentication.'false);
  257.                     exit();
  258.                 }
  259.                 return false;
  260.             }
  261.         
  262.             // Get the password parameter
  263.             $password $this->request->get_password($require);
  264.             if ($password == null{
  265.                 if ($require{
  266.                     $this->response->quick_output(-212'OBJECT_AUTH''Password cannot be empty.'false);
  267.                     exit();
  268.                 }
  269.                 return false;
  270.             }
  271.             
  272.             // Does the password contain an object UUID?
  273.             $parts explode('|'$password);
  274.             if (count($parts2{
  275.                 if ($require{
  276.                     $this->response->quick_output(-212'OBJECT_AUTH''Expected UUID and password, separated by pipe character.'false);
  277.                     exit();
  278.                 }
  279.                 return false;
  280.             }
  281.             
  282.             // Extract the parts
  283.             $objuuid $parts[0];
  284.             $objpwd $parts[1];
  285.             
  286.             // Make sure the password was provided
  287.             if (empty($objpwd)) {
  288.                 if ($require{
  289.                     $this->response->quick_output(-212'OBJECT_AUTH''Object-specific password cannot be empty.'false);
  290.                     exit();
  291.                 }
  292.                 return false;
  293.             }
  294.             
  295.             // Attempt to retreive a record matching the avatar and object UUID's
  296.             $rec get_record('sloodle_user_object''avuuid'$avuuid'objuuid'$objuuid);
  297.             if (!$rec{
  298.                 if ($require{
  299.                     $this->response->quick_output(-216'OBJECT_AUTH''Object not found in database.'false);
  300.                     exit();
  301.                 }
  302.                 return false;
  303.             }
  304.             
  305.             // Make sure the object is authorised
  306.             if (empty($rec->authorised|| $rec->authorised == "0"{
  307.                 if ($require{
  308.                     $this->response->quick_output(-214'OBJECT_AUTH''Object is not yet authorised.'false);
  309.                     exit();
  310.                 }
  311.                 return false;
  312.             }
  313.             
  314.             // Make sure the passwords match
  315.             if ($objpwd != $rec->password{
  316.                 if ($require{
  317.                     $this->response->quick_output(-213'OBJECT_AUTH''Object-specific password was invalid.'false);
  318.                     exit();
  319.                 }
  320.                 return false;
  321.             }
  322.             
  323.             // Everything looks fine
  324.             return true;
  325.         }
  326.         
  327.         
  328.         /**
  329.         * Validates the user account and enrolment (ensures there is an avatar linked to a VLE account, and that the VLE account is enrolled in the current course).
  330.         * Attempts auto-registration/enrolment if that is allowed and required, and logs-in the user.
  331.         * Server access level is checked if it is specified in the request parameters.
  332.         * If the request indicates that it relates to an object, then the validation fails.
  333.         * Note: if you only require to ensure that an avatar is registered, then use {@link validate_avatar()}.
  334.         * @param bool $require If true, the script will be terminated with an error message if validation fails
  335.         * @param bool $suppress_autoreg If true, auto-registration will be completely suppressed for this function call
  336.         * @param bool $suppress_autoenrol If true, auto-enrolment will be completely suppressed for this function call
  337.         * @return bool Returns true if validation and/or autoregistration were successful. Returns false on failure (unless $require was true).
  338.         * @see SloodleSession::validate_avatar()
  339.         */
  340.         function validate_user($require true$suppress_autoreg false$suppress_autoenrol false)
  341.         {
  342.             // Is it an object request?
  343.             if ($this->request->is_object_request()) {
  344.                 if ($require{
  345.                     $this->response->quick_output(-301'USER_AUTH''Cannot validate object as user.'false);
  346.                     exit();
  347.                 }
  348.                 return false;
  349.             }
  350.             
  351.             // Was a server access level specified in the request?
  352.             $sal $this->request->get_server_access_level(false);
  353.             if ($sal != null{
  354.                 // Check what level was specified
  355.                 $sal = (int)$sal;
  356.                 $allowed false;
  357.                 $reason 'Unknown.';
  358.                 switch ($sal{
  359.                 case SLOODLE_SERVER_ACCESS_LEVEL_PUBLIC:
  360.                     // Always allowed
  361.                     $allowed true;
  362.                     break;
  363.                 
  364.                 case SLOODLE_SERVER_ACCESS_LEVEL_COURSE:
  365.                     // Is a course already loaded?
  366.                     if (!$this->course->is_loaded()) {
  367.                         $reason 'No course loaded.';
  368.                         break;
  369.                     }
  370.                 
  371.                     // Was a user account already fully loaded?
  372.                     if ($this->user->is_avatar_linked()) {
  373.                         // Is the user enrolled on the current course?
  374.                         if ($this->user->is_enrolled($this->course->get_course_id())) $allowed true;
  375.                         else $reason 'User not enrolled in course.';
  376.                     else {
  377.                         $reason 'User not registered on site.';
  378.                     }
  379.                     break;
  380.                     
  381.                 case SLOODLE_SERVER_ACCESS_LEVEL_SITE:
  382.                     // Was a user account already fully loaded?
  383.                     if ($this->user->is_avatar_linked()) $allowed true;
  384.                     else $reason 'User not registered on site.';
  385.                     break;
  386.                     
  387.                 case SLOODLE_SERVER_ACCESS_LEVEL_STAFF:
  388.                     // Is a course already loaded?
  389.                     if (!$this->course->is_loaded()) {
  390.                         $reason 'No course loaded.';
  391.                         break;
  392.                     }
  393.                 
  394.                     // Was a user account already fully loaded?
  395.                     if ($this->user->is_avatar_linked()) {
  396.                         // Is the user staff on the current course?
  397.                         if ($this->user->is_staff($this->course->get_course_id())) $allowed true;
  398.                         else $reason 'User not staff in course.';
  399.                     else {
  400.                         $reason 'User not registered on site.';
  401.                     }
  402.                     break;
  403.                     
  404.                 default:
  405.                     // Unknown access level
  406.                     $reason 'Access level not recognised';
  407.                     break;
  408.                 }
  409.                 
  410.                 // Was the user blocked by access level?
  411.                 if (!$allowed{
  412.                     if ($require{
  413.                         $this->response->quick_output(-331'USER_AUTH'$reasonfalse);
  414.                         exit();
  415.                     }
  416.                     return false;
  417.                 }
  418.             }
  419.         
  420.         // REGISTRATION //
  421.         
  422.             // Make sure a the course is loaded
  423.             if (!$this->course->is_loaded()) {
  424.                 if ($require{
  425.                     $this->response->quick_output(-511'COURSE''Cannot validate user - no course data loaded.'false);
  426.                     exit();
  427.                 }
  428.                 return false;
  429.             }
  430.         
  431.             // Is the user already loaded?
  432.             if (!$this->user->is_avatar_linked())
  433.             {
  434.                 // If an avatar is loaded, but the user isn't, then we probably have a deleted Moodle user
  435.                 if ($this->user->is_avatar_loaded(== true && $this->user->is_user_loaded(== false{
  436.                     $this->response->quick_output(-301'USER_AUTH''Avatar linked to deleted user account'false);
  437.                     exit();
  438.                 }
  439.             
  440.                 // Make sure avatar details were provided
  441.                 $uuid $this->request->get_avatar_uuid(false);
  442.                 $avname $this->request->get_avatar_name(false);
  443.                 // Is validation required?
  444.                 if ($require{
  445.                     // Check the UUID
  446.                     if (empty($uuid)) {
  447.                         $this->response->quick_output(-311'USER_AUTH''User UUID required'false);
  448.                         exit();
  449.                     }
  450.                     // Check the name
  451.                     if (empty($avname)) {
  452.                         $this->response->quick_output(-311'USER_AUTH''Avatar name required'false);
  453.                         exit();
  454.                     }
  455.                 else if (empty($uuid|| empty($avname)) {
  456.                     // If there was a problem, just stop
  457.                     return false;
  458.                 }
  459.             
  460.                 // Ensure autoreg is not suppressed, and that it is permitted on that course and on the site
  461.                 if ($suppress_autoreg == true || $this->course->check_autoreg(== false{
  462.                     if ($require{
  463.                         $this->response->quick_output(-321'USER_AUTH''User not registered, and auto-registration of users was not permitted'false);
  464.                         exit();
  465.                     }
  466.                     return false;
  467.                 }
  468.                 
  469.                 // It is important that we also check auto-enrolment here.
  470.                 // If that is not enabled, but the call here requires it, then there is no point registering the user.
  471.                 if ($suppress_autoenrol == true || $this->course->check_autoenrol(== false{
  472.                     if ($require{
  473.                         $this->response->quick_output(-421'USER_ENROL''User not enrolled, and auto-enrolment of users was not permitted'false);
  474.                         exit();
  475.                     }
  476.                     return false;
  477.                 }
  478.             
  479.                 // Is there an avatar loaded?
  480.                 if (!$this->user->is_avatar_loaded()) {
  481.                     // Add the avatar details, linked to imaginary user 0
  482.                     if (!$this->user->add_linked_avatar(0$uuid$avname)) {
  483.                         if ($require{
  484.                             $this->response->quick_output(-322'USER_AUTH''Failed to add new avatar'false);
  485.                             exit();
  486.                         }
  487.                         return false;
  488.                     }
  489.                 }
  490.                 
  491.                 // If we reached here then we definitely have an avatar
  492.                 // Create a matching Moodle user
  493.                 $password $this->user->autoregister_avatar_user();
  494.                 if ($password === FALSE{
  495.                     if ($require{
  496.                         $this->response->quick_output(-322'USER_AUTH''Failed to register new user account'false);
  497.                         exit();
  498.                     }
  499.                     return false;
  500.                 }
  501.                 
  502.                 // Add a side effect code to our response data
  503.                 $this->response->add_side_effect(322);
  504.                 // The user needs to be notified of their new username/password
  505.                 if (isset($_SERVER['HTTP_X_SECONDLIFE_OBJECT_KEY'])) {
  506.                     sloodle_login_notification($_SERVER['HTTP_X_SECONDLIFE_OBJECT_KEY']$uuid$this->user->get_username()$password);
  507.                 }
  508.             }
  509.             
  510.         // ENROLMENT //
  511.             
  512.             // Is the user already enrolled on the course?
  513.             if (!$this->user->is_enrolled($this->course->get_course_id())) {
  514.                 // Ensure auto-enrolment is not suppressed, and that it is permitted on that course and on the site
  515.                 if ($suppress_autoenrol == true || $this->course->check_autoenrol(== false{
  516.                     if ($require{
  517.                         $this->response->quick_output(-421'USER_ENROL''Auto-enrolment of users was not permitted'false);
  518.                         exit();
  519.                     }
  520.                     return false;
  521.                 }
  522.                 
  523.                 // Attempt to enrol the user
  524.                 if (!$this->user->enrol()) {
  525.                     if ($require{
  526.                         $this->response->quick_output(-422'USER_ENROL''Auto-enrolment failed'false);
  527.                         exit();
  528.                     }
  529.                     return false;
  530.                 }
  531.                 
  532.                 // Add a side effect code to our response data
  533.                 $this->response->add_side_effect(422);
  534.             }
  535.             
  536.             // Make sure the user is logged-in
  537.             return ($this->user->login());
  538.         }
  539.         
  540.         /**
  541.         * Validate the avatar specified in the request, to ensure it is registered to a Moodle account.
  542.         * (Also ensures that avatar details were in fact provided in the request).
  543.         * This is effectively a less strict version of {@link validated_user()}, which also checks enrolment and such like.
  544.         * This function will NOT perform auto-registration or auto-enrolment.
  545.         * @param bool $require If true, the script will be terminated with an error message if validation fails
  546.         * @return bool Returns true if validation was successful. Returns false on failure (unless $require was true).
  547.         * @see SloodleSession::validate_user()
  548.         */
  549.         function validate_avatar$require true )
  550.         {
  551.             // Attempt to fetch avatar details
  552.             $sloodleuuid $this->request->get_avatar_uuid(false);
  553.             $sloodleavname $this->request->get_avatar_name(false);
  554.             // We need at least one of the values
  555.             if (empty($sloodleuuid&& empty($sloodleavname)) {
  556.                 if ($require{
  557.                     $this->response->quick_output(-311'USER_AUTH''Require avatar UUID and/or name.'false);
  558.                     exit();
  559.                 }
  560.                 return false;
  561.             }
  562.             
  563.             // Attempt to find an avatar matching the given details
  564.             $rec false;
  565.             if (!empty($sloodleuuid)) $rec get_record('sloodle_users''uuid'$sloodleuuid);
  566.             if (!$rec$rec get_record('sloodle_users''avname'$sloodleavname);
  567.             // Did we find a matching entry?
  568.             if (!$rec{
  569.                 // No - avatar is not validated
  570.                 if ($require{
  571.                     $this->response->quick_output(-321'USER_AUTH''Require avatar UUID and/or name.'false);
  572.                     exit();
  573.                 }
  574.                 return false;
  575.             }
  576.             
  577.             return true;
  578.         }
  579.         
  580.         
  581.         
  582.         //... Add functions for verifying user access to resources?
  583.     }
  584.     
  585.  
  586. ?>

Documentation generated on Mon, 07 Jul 2008 12:33:15 +0100 by phpDocumentor 1.4.0