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

Documentation generated on Fri, 17 Jul 2009 11:02:30 +0100 by phpDocumentor 1.4.0