Commit 1d8ff75b by Joe Blaylock Committed by stv

Deny self-service certificates for anonymous users

Bugfix for the following condition:
When anonymous (e.g., non-registered) access is enabled for a course,
and that course also uses the /request_certificate endpoint (e.g., via
the "Grade Me" button), anonymous users could request to be graded and
given certificates.

As a matter of policy, Stanford doesn't want to give certificates to
anonymous students.

Also adds defensive error checks in several more places.
parent bfc5c4c8
......@@ -2,31 +2,33 @@
metadata:
display_name: (Grade Me!) Button
data: |
<p>By clicking the button below, you assert that you have completed the course in its entirety.</p>
<!-- <form action=/request_certificate method=POST>
<input type=hidden name=student_id value=103 />
<input type=hidden name=course_id value="OpenEdX/200/Stanford_Sandbox" />
<input type=submit name="hoo" />
</form> -->
<input type=button value="Yes, I Agree." id="User_Verify_Button" style="margin-bottom: 20px;" />
<p id="verify_button_by_clicking_msg">By clicking the button below, you assert that you have completed the course in its entirety.</p>
<p><input type=button value="Yes, I Agree." id="User_Verify_Button" style="margin-bottom: 20px;" /></p>
<p class="verify-button-success-text" style="font-weight: bold; color: #008200;"></p>
<script type="text/javascript">
var success_message = "Your grading and certification request has been received, <br />if you have passed, your certificate should be available in the next 20 minutes.";
document.getElementById('User_Verify_Button').addEventListener("click",
function(event) {
(function(event) {
var linkcontents = $('a.user-link').contents();
$.ajax({
type: 'POST',
url: '/request_certificate',
data: {'course_id': $$course_id},
success: function(data) {
$('.verify-button-success-text').html(success_message);
}
var failure_message = "We're sorry; users who haven't created accounts and registered for the course may not receive Statements of Accomplishment.";
// for actual value of username, use scraped_username.split(':')[1].trim(); to get actual value
var scraped_username = $('li.primary a.user-link').text();
if (scraped_username) {
document.getElementById('User_Verify_Button').addEventListener("click",
function(event) {
(function(event) {
$.ajax({
type: 'POST',
url: '/request_certificate',
data: {'course_id': $$course_id},
success: function(data) {
$('.verify-button-success-text').html(success_message);
}
});
}).call(document.getElementById('User_Verify_Button'), event);
});
}).call(document.getElementById('User_Verify_Button'), event);
});
} else {
$('#verify_button_by_clicking_msg').html(failure_message);
$('#User_Verify_Button').remove();
};
</script>
"""URL handlers related to certificate handling by LMS"""
from dogapi import dog_stats_api
import json
import logging
......@@ -9,16 +10,13 @@ from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from capa.xqueue_interface import XQUEUE_METRIC_NAME
from certificates.models import certificate_status_for_student, CertificateStatuses, GeneratedCertificate
from certificates.queue import XQueueCertInterface
from student.models import UserProfile
from xmodule.course_module import CourseDescriptor
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.locations import SlashSeparatedCourseKey
from certificates.models import certificate_status_for_student, CertificateStatuses, GeneratedCertificate
from certificates.queue import XQueueCertInterface
from xmodule.course_module import CourseDescriptor
from xmodule.modulestore.django import modulestore
use_cme = settings.FEATURES.get('USE_CME_REGISTRATION', False)
if use_cme:
......@@ -37,26 +35,38 @@ def request_certificate(request):
at the end of a course run, so that we can be sure users get graded and
then if and only if they pass, do they get a certificate issued.
"""
if request.method == "POST":
if request.user.is_authenticated():
xqci = XQueueCertInterface()
username = request.user.username
student = User.objects.get(username=username)
course_key = SlashSeparatedCourseKey.from_deprecated_string(request.POST.get('course_id'))
course = modulestore().get_course(course_key, depth=2)
title = 'None'
if use_cme:
titlelist = CmeUserProfile.objects.filter(user=student).values('professional_designation')
if len(titlelist):
print "DEBUG: {}".format(repr(titlelist))
title = titlelist[0]['professional_designation']
status = certificate_status_for_student(student, course_key)['status']
if status in [CertificateStatuses.unavailable, CertificateStatuses.notpassing, CertificateStatuses.error]:
logger.info('Grading and certification requested for user {} in course {} via /request_certificate call'.format(username, course_key))
status = xqci.add_cert(student, course_key, course=course)
return HttpResponse(json.dumps({'add_status': status}), mimetype='application/json')
return HttpResponse(json.dumps({'add_status': 'ERRORANONYMOUSUSER'}), mimetype='application/json')
# Memoize user information; return error if it's invalid
user = None
username = ''
try:
user = request.user
username = user.username
except AttributeError:
return HttpResponse(json.dumps({'add_status': 'error', 'error': 'ERRORBADREQUEST'}), mimetype='application/json')
# It is an error to hit this endpoint with anything but a POST
if request.method != "POST":
return HttpResponse(json.dumps({'add_status': 'error', 'error': 'ERRORNOPOST'}), mimetype='application/json')
# It is an error to hit this endpoint as an anonymous/nonregistered user
if not (user.is_authenticated() and UserProfile.has_registered(user)):
return HttpResponse(json.dumps({'add_status': 'error', 'error': 'ERRORANONYMOUSUSER'}), mimetype='application/json')
xq = XQueueCertInterface()
student = User.objects.get(username=username)
course_key = SlashSeparatedCourseKey.from_deprecated_string(request.POST.get('course_id'))
course = modulestore().get_course(course_key, depth=2)
title = 'None'
if use_cme:
titlelist = CmeUserProfile.objects.filter(user=student).values('professional_designation')
if len(titlelist):
title = titlelist[0]['professional_designation']
status = certificate_status_for_student(student, course_key)['status']
if status in [CertificateStatuses.unavailable, CertificateStatuses.notpassing, CertificateStatuses.error]:
logger.info('Grading and certification requested for user {} in course {} via /request_certificate call'.format(username, course_key))
status = xq.add_cert(student, course_key, course=course, title=title)
return HttpResponse(json.dumps({'add_status': status, 'error': ''}), mimetype='application/json')
@csrf_exempt
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment