Ray Morris的帖子

Here are the key pieces from what I used to implement this.  These are copy / paste snippets from our authentication module.  Note this does not CREATE the user id Moodle, it logs in a user created earlier when they enrolled.  The key part of the whole thing is the PK parameter sent by the sending site.  It's HMAC of the entire query string plus a pre-shared key. That validates that the query string came from your partner site. Note also that the hashed parameters include a timestamp.


function user_login($username, $password){

$extusername= core_text::convert($username, 'utf-8', $this->config->extencoding);

$extpassword= core_text::convert($password, 'utf-8', $this->config->extencoding);

$teexsso= Teexsso::getInstance();

return$teexsso->checkhmac();

}


function loginpage_hook(){

$teexsso= Teexsso::getInstance();

return$teexsso->loginpage_hook();

}


class Teexsso {


protectedstatic$instance;

public$isvalid=false;


publicstaticfunction getInstance(){

if(!isset(self::$instance)){

self::$instance=new Teexsso;

}

returnself::$instance;

}


/**

     * Protected constructor to prevent creating a new instance of the

     * *Singleton* via the `new` operator from outside of this class.

     */

protectedfunction__construct(){

}


function loginpage_hook(){

global$DB;

global$SESSION;

global$CFG;

global$frm;


if(empty($_GET['PK'])){

returnfalse;

}

$this->username =$this->checkhmac();

if($this->username){

if(empty($frm)||($frm)){

$frm=newstdClass;

}

$frm->username =$this->username;

$frm->password = 'x';

$this->isvalid =true;

if($_GET['C']){

$courseid=$DB->get_field('course', 'id', array('idnumber' =>$_GET['C']));

$SESSION->wantsurl =$CFG->wwwroot . '/course/view.php?id=' .$courseid;

}

}

    }



function checkhmac(){

global$DB;

if($this->isvalid &&!empty($this->username)){

return$this->username;

}


if(empty($_GET['PK'])){

returnfalse;

}

// This must use $_GET rather than _param to ensure variables are from the query string, and therefore in the hmac.

// We wouldn't want a different username set via POST.

$hmactry=$_GET['PK'];

$this->username =$_GET['username'];

$time=$_GET['T'];


if((!$hmactry)||(abs($time-time())>600)){

returnfalse;

}


$key= 'RANDOM_STRING_YOU_MUST_CHANGE_THIS';

$paramstr=preg_replace('/&*PK=[A-Za-z0-9]+/', '', $_SERVER['QUERY_STRING']);

$paramstr=preg_replace('/^\?/', '', $paramstr);

$hmaccorrect=strtoupper(hash_hmac('sha256','?' .$paramstr, $key));

if($hmactry!==$hmaccorrect){

$this->isvalid =false;

returnfalse;

}


$this->isvalid =true;

if(!empty($_GET['txid'])){

$txiduser=$DB->get_field('user', 'username', array('idnumber' =>$_GET['txid']));

if($txiduser){

$this->username =$txiduser;

}

}

global$user;

$user= get_complete_user_data('username', $this->username);

return$this->username;

}


}

Be very, very careful - it's easy to get authentication wrong, and it is a target for attack.  The above code, while written by a security professional, has not yet passed my own final security analysis, much less third-party review.


> Hey Tim, do you think that MDL-41924 will get some traction? Or what is needed to get work done on it?


It will, at some point in time.  As Tim pointed out, it's a hard problem. I happen to like problems exactly like this one - hard, but not impossible or logically invalid, and it's one single hard problem, not a bunch of interrelated problems.  When I get time, I'll probably need to work on this. That may be soon, after I finish polishing up the paper copy quiz module. Or something else will come up and this will get pushed off until later.  At some point in time, I'm fairly sure I'll have to solve this if nobody else has done so first.

I just now putting the finishing touches on updating module which does just this, by extending Quiz.  Teachers can grade manually, but mainly it's designed for use with Scantrons and automatic grading.  You might want to see if what we have will work for you.  I suspect it may meet your business requirements, though it might not match exactly the process you had in mind. Even if not, it would be a good base to start from and you could do your own modifications.  Here's our current code, which will be updated further in the next few weeks:

https://github.com/MorrisR2/moodle-quiz_papercopy/tree/wkhtml27

Ours is an updated fork of BU's older code:
https://github.com/bumoodle/moodle-quiz_papercopy


The Moodle Quiz module is very flexible, with many different submodules which extend it's functionality.  Using it with using a submodule like ours to add the functionality you need would probably be a very good idea.


This is another module which is similar, but it appears that it does NOT use the standard Quiz module as base.  Therefore it will probably be missing many features available in Quiz and features which will be added to Quiz in the future:

https://docs.moodle.org/28/en/mod/offlinequiz/mod


Writing a new module for Moodle from scratch is not easy, not at all.  Because Moodle is so flexible, both in terms of modularity and being able to use any RDMS, run on any platform, etc., programming within Moodle is quite a bit more disciplined than general PHP programming.  In many ways, learning to design and author software to run within Moodle is very similar to learning a new programming language altogether.   For example, you don't use any of the PHP database functions you might already be familiar with - Moodle provides platform-neutral database functions of it's own which you use.  You shouldn't generally use output functions like "echo" - Moodle has an entire output API or two for you to use.  For these reasons, I'd strongly suggest looking carefully if one of the existing modules covers the needs of what you actually need to accomplish, temporarily putting aside possible process you outlined in your post.  Using an existing module will save you a lot of time and aggravation compared to building, and then maintaining, your own separate module.


the bit I don't understand is why someone would pay for something which is already available free. But that's the nature of how some people think/work I guess.


Other than the case where they simply don't know they can get it for free, the person selling it might package it such that it is more convenient or useful in some way. One example is FOSS software which is readily available for normal Linux, but hasn't been packaged as an Android app Android is a form of Linux).  Someone might pay 99 cents for a copy that's in the app sore rather than go download the source code, cross-compile it for Android, etc.


An example for Moodle specifically would be if someone chose a bunch of third-party plugins, a theme, and configuration settings to set up Moodle to work well for ____.  Maybe for higher-stakes assessments, or for a certain field or whatever.  They might then sell "Mdl for Medical" for $199, all setup and ready to go for institutions teaching in the medical field. Or they might sell it pre-packaged with all of the materials required to comply with US labor laws, like non-discrimination notices, etc., and setup for easy compliance reporting, showing tat all employees had viewed the notices.