--- loncom/interface/createaccount.pm 2012/02/13 17:22:56 1.40.2.5.2.6 +++ loncom/interface/createaccount.pm 2012/05/18 04:31:05 1.51 @@ -3,7 +3,7 @@ # institutional log-in ID (institutional authentication required - localauth # or kerberos) or an e-mail address. # -# $Id: createaccount.pm,v 1.40.2.5.2.6 2012/02/13 17:22:56 raeburn Exp $ +# $Id: createaccount.pm,v 1.51 2012/05/18 04:31:05 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -39,12 +39,14 @@ use Apache::lonhtmlcommon; use Apache::lonlocal; use Apache::lonauth; use Apache::resetpw; -use Captcha::reCAPTCHA; +use Authen::Captcha; use DynaLoader; # for Crypt::DES version use Crypt::DES; use LONCAPA qw(:DEFAULT :match); use HTML::Entities; +#TODO this module needs documentation + sub handler { my $r = shift; &Apache::loncommon::content_type($r,'text/html'); @@ -57,8 +59,6 @@ sub handler { my $sso_username = $r->subprocess_env->get('REDIRECT_SSOUserUnknown'); my $sso_domain = $r->subprocess_env->get('REDIRECT_SSOUserDomain'); - my $privkey = $r->subprocess_env->get('reCAPTCHA_PRIVATE'); - my $pubkey = $r->subprocess_env->get('reCAPTCHA_PUBLIC') &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['token','courseid']); &Apache::lonacc::get_posted_cgi($r); @@ -67,14 +67,8 @@ sub handler { if ($sso_username ne '' && $sso_domain ne '') { $domain = $sso_domain; } else { - $domain = &Apache::lonnet::default_login_domain(); - if (defined($env{'form.courseid'})) { - if (&validate_course($env{'form.courseid'})) { - if ($env{'form.courseid'} =~ /^($match_domain)_($match_courseid)$/) { - $domain = $1; - } - } - } + ($domain, undef) = Apache::lonnet::is_course($env{'form.courseid'}); + $domain ||= &Apache::lonnet::default_login_domain(); } my $domdesc = &Apache::lonnet::domain($domain,'description'); my $contact_name = &mt('LON-CAPA helpdesk'); @@ -100,9 +94,7 @@ sub handler { } my ($js,$courseid,$title); - if (defined($env{'form.courseid'})) { - $courseid = &validate_course($env{'form.courseid'}); - } + $courseid = Apache::lonnet::is_course($env{'form.courseid'}); if ($courseid ne '') { $js = &catreturn_js(); $title = 'Self-enroll in a LON-CAPA course'; @@ -129,18 +121,15 @@ sub handler { &print_footer($r); return OK; } else { - $start_page = - &Apache::loncommon::start_page($title,$js, - {'no_inline_link' => 1,}); + $start_page = &Apache::loncommon::start_page($title,$js); &print_header($r,$start_page,$courseid); $r->print($output); &print_footer($r); return OK; } } - $start_page = - &Apache::loncommon::start_page($title,$js, - {'no_inline_link' => 1,}); + $start_page = &Apache::loncommon::start_page($title,$js); + my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$domain); my ($cancreate,$statustocreate) = &get_creation_controls($domain,$domconfig{'usercreation'}); @@ -148,7 +137,7 @@ sub handler { &print_header($r,$start_page,$courseid); my $output = '

'.&mt('Account creation unavailable').'

'. ''. - &mt('Creation of a new user account using an e-mail address or an institutional log-in ID as username is not permitted for [_1].',$domdesc).'

'; + &mt('Creation of a new user account using an e-mail address or an institutional log-in ID as username is not permitted at this institution ([_1]).',$domdesc).'

'; $r->print($output); &print_footer($r); return OK; @@ -200,7 +189,7 @@ sub handler { if ($env{'form.phase'} eq 'username_activation') { (my $result,$output,$nostart) = &username_activation($r,$env{'form.uname'},$domain,$domdesc, - $lonhost,$courseid); + $courseid); if ($result eq 'ok') { if ($nostart) { return OK; @@ -227,7 +216,7 @@ sub handler { $output = &process_email_request($env{'form.useremail'},$domain,$domdesc, $contact_name,$contact_email,$cancreate, $lonhost,$domconfig{'usercreation'}, - $courseid,$privkey); + $courseid); } elsif (!$token) { &print_header($r,$start_page,$courseid); my $now=time; @@ -240,22 +229,13 @@ sub handler { $r->print(&javascript_validmail()); } $output = &print_username_form($domain,$domdesc,$cancreate,$now,$lonhost, - $courseid,$pubkey); + $courseid); } $r->print($output); &print_footer($r); return OK; } -sub get_custom_name { - my ($domain) = @_; - if ($domain eq 'relate') { - return 'Mechanics Online'; - } else { - return lc($domain); - } -} - sub print_header { my ($r,$start_page,$courseid) = @_; $r->print($start_page); @@ -307,17 +287,6 @@ sub selfenroll_crumbs { return; } -sub validate_course { - my ($courseid) = @_; - my ($cdom,$cnum) = ($courseid =~ /^($match_domain)_($match_courseid)$/); - if (($cdom ne '') && ($cnum ne '')) { - if (&Apache::lonnet::is_course($cdom,$cnum)) { - return ($courseid); - } - } - return; -} - sub javascript_setforms { my ($now) = @_; my $js = < 'username', - udom => 'domain', - uemail => 'E-mail address in LON-CAPA', - proc => 'Proceed', - crea => 'Create account with a username provided by this institution', - crlc => 'Create LON-CAPA account', - type => 'Type in your log-in ID and password to find out.', - sign => 'You are about to sign-up for the Mechanics Online course.', - plse => 'Please enter a valid e-mail address below.', - inst => 'Instructions on how to activate your account will be sent to the e-mail address you provide.', - aftr => 'After completing the activation process you will have access to a "self test" that will help you assess your readiness for the course.', - thes => 'The same account will be used for access to the Mechanics Online course, once it becomes available on March 1, 2012', - ); + my ($domain,$domdesc,$cancreate,$now,$lonhost,$courseid) = @_; + my %lt = &Apache::lonlocal::texthash( + unam => 'username', + udom => 'domain', + uemail => 'E-mail address in LON-CAPA', + proc => 'Proceed'); my $output; if (ref($cancreate) eq 'ARRAY') { if (grep(/^login$/,@{$cancreate})) { my %domdefaults = &Apache::lonnet::get_domain_defaults($domain); if ((($domdefaults{'auth_def'} =~/^krb/) && ($domdefaults{'auth_arg_def'} ne '')) || ($domdefaults{'auth_def'} eq 'localauth')) { - $output = '

'.$lt{'crea'}.'

'; - my $submit_text = $lt{'crlc'}; - $output .= &mt('If you already have a log-in ID at this institution,[_1] you may be able to use it for LON-CAPA.','
'). - '

'.$lt{'type'}.'

'; + $output = '

'.&mt('Create account with a username provided by this institution').'

'; + my $submit_text = &mt('Create LON-CAPA account'); + $output .= &mt('If you already have a log-in ID at this institution,[_1] you may be able to use it for LON-CAPA.','
').'

'.&mt('Type in your log-in ID and password to find out.').'

'; $output .= &login_box($now,$lonhost,$courseid,$submit_text, $domain,'createaccount').'
'; } } if (grep(/^email$/,@{$cancreate})) { - $output .= '

'.&mt('Create account with an e-mail address as your username').'

'. - $lt{'sign'}.'
'. - $lt{'plse'}.'
'. - $lt{'inst'}.'
'. - $lt{'aftr'}.'
'. - $lt{'thes'}.'
'; - my $captchaform = &create_recaptcha($pubkey); + $output .= '

'.&mt('Create account with an e-mail address as your username').'

'; + my $captchaform = &create_captcha(); if ($captchaform) { - my $submit_text = &mt('Sign-up'); + my $submit_text = &mt('Request LON-CAPA account'); my $emailform = ''; if (grep(/^login$/,@{$cancreate})) { - $output .= &mt('Provide your e-mail address to sign up for a Mechanics Online account [_1]if you do not have a log-in ID at your institution.','
').'

'; + $output .= &mt('Provide your e-mail address to request a LON-CAPA account,[_1] if you do not have a log-in ID at your institution.','
').'

'; } else { $output .= '
'; } @@ -452,8 +399,7 @@ sub print_username_form { &Apache::lonhtmlcommon::row_closure(1). &Apache::lonhtmlcommon::row_title(&mt('Validation'), 'LC_pick_box_title')."\n". - $captchaform."\n". - &mt('If either word is hard to read, [_1] will replace them.','reCAPTCHA refresh').'

'; + $captchaform."\n".'

'; if ($courseid ne '') { $output .= ''."\n"; } @@ -500,7 +446,6 @@ sub login_box { my $unameform = ''; my $upassform = ''; $output .= '
'."\n". - ''. &Apache::lonhtmlcommon::start_pick_box()."\n". &Apache::lonhtmlcommon::row_title($titles{$context}, 'LC_pick_box_title')."\n". @@ -508,8 +453,18 @@ sub login_box { &Apache::lonhtmlcommon::row_closure(1)."\n". &Apache::lonhtmlcommon::row_title(&mt('Password'), 'LC_pick_box_title')."\n". - $upassform. - &Apache::lonhtmlcommon::row_closure(1). + $upassform; + if ($context eq 'selfenroll') { + my $udomform = ''; + $output .= &Apache::lonhtmlcommon::row_closure(1)."\n". + &Apache::lonhtmlcommon::row_title(&mt('Domain'), + 'LC_pick_box_title')."\n". + $udomform."\n"; + } else { + $output .= ''; + } + $output .= &Apache::lonhtmlcommon::row_closure(1). &Apache::lonhtmlcommon::row_title(). '
'."\n"; @@ -529,8 +484,8 @@ sub login_box { sub process_email_request { my ($useremail,$domain,$domdesc,$contact_name,$contact_email,$cancreate, - $server,$settings,$courseid,$privkey) = @_; - $useremail = lc($env{'form.useremail'}); + $server,$settings,$courseid) = @_; + $useremail = $env{'form.useremail'}; my $output; if (ref($cancreate) eq 'ARRAY') { if (!grep(/^email$/,@{$cancreate})) { @@ -545,22 +500,26 @@ sub process_email_request { my $uhome = &Apache::lonnet::homeserver($useremail,$domain); if ($uhome ne 'no_host') { $output = &invalid_state('existinguser',$domdesc, - $contact_name,$contact_email,'',$useremail); + $contact_name,$contact_email); return $output; } else { - my $captcha = Captcha::reCAPTCHA->new; - my $captcha_result = - $captcha->check_answer( - $privkey, - $ENV{'REMOTE_ADDR'}, - $env{'form.recaptcha_challenge_field'}, - $env{'form.recaptcha_response_field'}, - ); - # PRIVATE key from https://www.google.com/recaptcha - if (!$captcha_result->{is_valid}) { + my $code = $env{'form.code'}; + my $md5sum = $env{'form.crypt'}; + my %captcha_params = &captcha_settings(); + my $captcha = Authen::Captcha->new( + output_folder => $captcha_params{'output_dir'}, + data_folder => $captcha_params{'db_dir'}, + ); + my $captcha_chk = $captcha->check_code($code,$md5sum); + my %captcha_hash = ( + 0 => 'Code not checked (file error)', + -1 => 'Failed: code expired', + -2 => 'Failed: invalid code (not in database)', + -3 => 'Failed: invalid code (code does not match crypt)', + ); + if ($captcha_chk != 1) { $output = &invalid_state('captcha',$domdesc,$contact_name, - $contact_email); - + $contact_email,$captcha_hash{$captcha_chk}); return $output; } my $uhome=&Apache::lonnet::homeserver($useremail,$domain); @@ -623,13 +582,13 @@ sub send_token { if ($token !~ /^error/ && $token ne 'no_such_host') { my $esc_token = &escape($token); my $showtime = localtime(time); - my $mailmsg = &mt('A request was submitted on [_1] for creation of a [_2] account.',$showtime,$domdesc)."\n". + my $mailmsg = &mt('A request was submitted on [_1] for creation of a LON-CAPA account at the following institution: [_2].',$showtime,$domdesc).' '. &mt('To complete this process please open a web browser and enter the following URL in the address/location box: [_1]', - "\n\n".&Apache::lonnet::absolute_url().'/adm/createaccount?token='.$esc_token); + &Apache::lonnet::absolute_url().'/adm/createaccount?token='.$esc_token); my $result = &Apache::resetpw::send_mail($domdesc,$email,$mailmsg,$contact_name, $contact_email); if ($result eq 'ok') { - $msg .= &mt('A message has been sent to the e-mail address you provided.').'
'.&mt('The message includes the web address for the link you will use to complete the sign-up process.').'
'.&mt("The link included in the message will be valid for the next [_1]two[_2] hours.",'',''); + $msg .= &mt('A message has been sent to the e-mail address you provided.').'
'.&mt('The message includes the web address for the link you will use to complete the account creation process.').'
'.&mt("The link included in the message will be valid for the next [_1]two[_2] hours.",'',''); } else { $msg .= ''. &mt('An error occurred when sending a message to the e-mail address you provided.').'
'. @@ -660,12 +619,12 @@ sub process_mailtoken { ($data{'username'} =~ /^[^\@]+\@[^\@]+\.[^\@\.]+$/)) { if ($now - $data{'time'} < 7200) { if ($env{'form.phase'} eq 'createaccount') { - my ($result,$output) = &create_account($r,$domain,$lonhost, - $data{'username'},$domdesc); + my ($result,$output,$uhome) = + &create_account($r,$domain,$data{'username'},$domdesc); if ($result eq 'ok') { $msg = $output; my $shownow = &Apache::lonlocal::locallocaltime($now); - my $mailmsg = &mt('A [_1] account has been created [_2] from IP address: [_3]. If you did not perform this action or authorize it, please contact the [_4] ([_5]).',$domdesc,$shownow,$ENV{'REMOTE_ADDR'},$contact_name,$contact_email)."\n"; + my $mailmsg = &mt('A LON-CAPA account for the institution: [_1] has been created [_2] from IP address: [_3]. If you did not perform this action or authorize it, please contact the [_4] ([_5]).',$domdesc,$shownow,$ENV{'REMOTE_ADDR'},$contact_name,$contact_email)."\n"; my $mailresult = &Apache::resetpw::send_mail($domdesc,$data{'email'}, $mailmsg,$contact_name, $contact_email); @@ -674,9 +633,8 @@ sub process_mailtoken { } else { $msg .= &mt('An error occurred when sending e-mail to [_1] confirming creation of your LON-CAPA account.',$data{'username'}); } - my %form = &start_session($r,$data{'username'},$domain, - $lonhost,$data{'courseid'}, - $token); + &start_session($r,$data{'username'},$domain,$uhome, + $data{'courseid'},$token); $nostart = 1; $noend = 1; } else { @@ -703,34 +661,31 @@ sub process_mailtoken { } sub start_session { - my ($r,$username,$domain,$lonhost,$courseid,$token) = @_; - my %form = ( - uname => $username, - udom => $domain, - ); - my $firsturl = '/adm/roles'; - if (defined($courseid)) { - $courseid = &validate_course($courseid); - if ($courseid ne '') { - $form{'courseid'} = $courseid; - $firsturl = '/adm/selfenroll?courseid='.$courseid; - } - } + my ($r,$username,$domain,$uhome,$courseid,$token) = @_; + if ($r->dir_config('lonBalancer') eq 'yes') { - &Apache::lonauth::success($r,$form{'uname'},$form{'udom'}, - $lonhost,'noredirect',undef,\%form); - if ($token ne '') { - my $delete = &Apache::lonnet::tmpdel($token); - } + Apache::lonauth::success($r, $username, $domain, $uhome, + 'noredirect', undef, {}); + + Apache::lonnet::tmpdel($token) if $token; + $r->internal_redirect('/adm/switchserver'); } else { - &Apache::lonauth::success($r,$form{'uname'},$form{'udom'}, - $lonhost,$firsturl,undef,\%form); + $courseid = Apache::lonnet::is_course($courseid); + + Apache::lonauth::success($r, $username, $domain, $uhome, + ($courseid ? "/adm/selfenroll?courseid=$courseid" : '/adm/roles'), + undef, {}); } - return %form; -} + return; +} +# +# The screen that the user gets to create his or her account +# Desired username, desired password, etc +# Stores token to store DES-key and stage during creation session +# sub print_dataentry_form { my ($r,$domain,$lonhost,$include,$mailtoken,$now,$username,$start_page) = @_; my ($error,$output); @@ -810,6 +765,10 @@ ENDSERVERFORM return $output; } +# +# Retrieve rules for generating accounts from domain configuration +# Can the user make a new account or just self-enroll? + sub get_creation_controls { my ($domain,$usercreation) = @_; my (@cancreate,@statustocreate); @@ -848,9 +807,13 @@ sub get_creation_controls { } sub create_account { - my ($r,$domain,$lonhost,$username,$domdesc) = @_; + my ($r,$domain,$username,$domdesc) = @_; +# Get the token info my ($retrieved,$output,$upass) = &process_credentials($env{'form.logtoken'}, $env{'form.serverid'}); +# $retrieved is 'ok' if things worked +# $output is user error output +# $upass is the decrypted password # Error messages my $error = ''.&mt('Error:').' '; my $end = '

'; @@ -859,6 +822,7 @@ sub create_account { &Apache::loncommon::end_page(); if ($retrieved eq 'ok') { if ($env{'form.courseid'} ne '') { +# See if we are allowed to use this username per domain rules (number of characters, etc) my ($result,$userchkmsg) = &check_id($username,$domain,$domdesc); if ($result eq 'fail') { $output = $error.&mt('Invalid ID format').$end. @@ -869,22 +833,32 @@ sub create_account { } else { return ('fail',$error.$output.$end.$rtnlink); } - # Call modifyuser + # Yes! We can do this. Valid token, valid username format + # Create an internally authenticated account with password $upass + # if the account does not exist yet + # Assign student/staff number $env{'form.cid'}, first name, last name, etc my $result = &Apache::lonnet::modifyuser($domain,$username,$env{'form.cid'}, 'internal',$upass,$env{'form.cfirstname'}, $env{'form.cmiddlename'},$env{'form.clastname'}, $env{'form.cgeneration'},undef,undef,$username); $output = &mt('Generating user: [_1]',$result); + # Now that the user exists, we can have a homeserver my $uhome = &Apache::lonnet::homeserver($username,$domain); $output .= '
'.&mt('Home server: [_1]',$uhome).' '. &Apache::lonnet::hostname($uhome).'

'; - return ('ok',$output); + return ('ok',$output,$uhome); } sub username_validation { my ($r,$username,$domain,$domdesc,$contact_name,$contact_email,$courseid, $lonhost,$statustocreate) = @_; +# $username,$domain: for the user who needs to be validated +# $domdesc: full name of the domain (for error messages) +# $contact_name, $contact_email: name and email for user assistance (for error messages in &username_check +# $courseid: ID of the course that the user should be validated for, goes into start_session +# $statustocreate: -> inststatus in username_check ('faculty', 'staff', 'student', ...) + my ($retrieved,$output,$upass); $username= &LONCAPA::clean_username($username); @@ -899,7 +873,7 @@ sub username_validation { if ($uhome ne 'no_host') { my $result = &Apache::lonnet::authenticate($username,$upass,$domain); if ($result ne 'no_host') { - my %form = &start_session($r,$username,$domain,$lonhost,$courseid); + &start_session($r,$username,$domain,$uhome,$courseid); $output = '

'.&mt('A LON-CAPA account already exists for username [_1] at this institution ([_2]).',''.$username.'',$domdesc).'
'.&mt('The password entered was also correct so you have been logged in.'); return ('existingaccount',$output); } else { @@ -1053,7 +1027,7 @@ sub username_check { } sub username_activation { - my ($r,$username,$domain,$domdesc,$lonhost,$courseid) = @_; + my ($r,$username,$domain,$domdesc,$courseid) = @_; my $output; my $error = ''.&mt('Error:').' '; my $end = '

'; @@ -1123,7 +1097,8 @@ sub username_activation { if ($result eq 'ok') { my $delete = &Apache::lonnet::tmpdel($env{'form.authtoken'}); $output = &mt('A LON-CAPA account has been created for username: [_1] in domain: [_2].',$username,$domain); - my %form = &start_session($r,$username,$domain,$lonhost,$courseid); + my $uhome=&Apache::lonnet::homeserver($username,$domain,'true'); + &start_session($r,$username,$domain,$uhome,$courseid); my $nostart = 1; return ('ok',$output,$nostart); } else { @@ -1139,6 +1114,9 @@ sub username_activation { sub check_id { my ($username,$domain,$domdesc) = @_; # Check ID format + # Is $username in an okay format for $domain + # (right number of characters, special characters, etc - follow domain rules)? + # $domdesc is just used for user error messages my (%alerts,%rulematch,%inst_results,%curr_rules,%checkhash); my %checks = ('id' => 1); %{$checkhash{$username.':'.$domain}} = ( @@ -1166,16 +1144,12 @@ sub check_id { } sub invalid_state { - my ($error,$domdesc,$contact_name,$contact_email,$msgtext,$useremail) = @_; + my ($error,$domdesc,$contact_name,$contact_email,$msgtext) = @_; my $msg = '

'.&mt('Account creation unavailable').'

'; if ($error eq 'baduseremail') { $msg .= &mt('The e-mail address you provided does not appear to be a valid address.'); } elsif ($error eq 'existinguser') { - my $uname = &HTML::Entities::encode($useremail); - $msg .= &mt('The e-mail address you provided is already in use as a username in LON-CAPA at this institution.').'

'.&mt('You can either:').'
    '. - '
  • '.&mt('Return to the [_1]log-in page[_2] and enter your password.','','').'
  • '. - '
  • '.&mt('or, if you do not remember your password, visit the "[_1]Forgot your password?[_2]" page.','',''). - '
'; + $msg .= &mt('The e-mail address you provided is already in use as a username in LON-CAPA at this institution.'); } elsif ($error eq 'userrules') { $msg .= &mt('Username rules at this institution do not allow the e-mail address you provided to be used as a username.'); } elsif ($error eq 'userformat') { @@ -1211,12 +1185,37 @@ sub linkto_email_help { return $msg; } -sub create_recaptcha { - my ($pubkey) = @_; - my $captcha = Captcha::reCAPTCHA->new; - # generate PUBLICKEY from https://www.google.com/recaptcha - return $captcha->get_options_setter({theme => 'white'})."\n". - $captcha->get_html($pubkey); +sub create_captcha { + my ($output_dir,$db_dir) = @_; + my %captcha_params = &captcha_settings(); + my ($output,$maxtries,$tries) = ('',10,0); + while ($tries < $maxtries) { + $tries ++; + my $captcha = Authen::Captcha->new ( + output_folder => $captcha_params{'output_dir'}, + data_folder => $captcha_params{'db_dir'}, + ); + my $md5sum = $captcha->generate_code($captcha_params{'numchars'}); + + if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') { + $output = ''."\n". + &mt('Type in the letters/numbers shown below').' '. + '
'. + ''; + last; + } + } + return $output; +} + +sub captcha_settings { + my %captcha_params = ( + output_dir => $Apache::lonnet::perlvar{'lonCaptchaDir'}, + www_output_dir => "/captchaspool", + db_dir => $Apache::lonnet::perlvar{'lonCaptchaDb'}, + numchars => '5', + ); + return %captcha_params; } sub getkeys { @@ -1256,6 +1255,12 @@ ENDSERVERFORM } sub process_credentials { +# +# Fetches the information from the logtoken via tmpget +# Token contains the DES-key and the stage of the process (would only be "createaccount") +# $lonhost in this routine is *not* necessarily the machine that this runs on, +# but $env{'form.serverid'}, the machine that issued the token. +# my ($logtoken,$lonhost) = @_; my $tmpinfo=Apache::lonnet::reply('tmpget:'.$logtoken,$lonhost); my ($retrieved,$output,$upass); @@ -1277,6 +1282,10 @@ sub process_credentials { } else { $output = &mt('Unable to retrieve your log-in information - unexpected context'); } +# $retrieved is 'ok' if retrieved okay +# $output is screen output for the user +# $upass is $env{'form.upass'}, decrypted with the DES-key, if stage was 'createaccount' + return ($retrieved,$output,$upass); } 500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.