def test_report_success(self): record = Record(student_id=self.student_id) record.state = Record.LOCKED record.save() vote_token = AuthToken.generate(self.student_id, str(self.station.external_id), '70') vote_token.save() url = reverse('report') data = { 'uid': self.student_id, 'vote_token': vote_token.code, 'token': self.token, 'api_key': settings.API_KEY, 'version': settings.API_VERSION, } response = self.client.post(url, data) self.assertEqual(response.data, {'status': 'success'})
def test_complete_success(self): record = Record(student_id=self.student_id) record.state = Record.VOTING record.save() token = AuthToken.generate(self.student_id, str(self.station.external_id), '70') token.save() url = 'https://{0}{1}?callback={2}'.format( settings.CALLBACK_DOMAIN, reverse('callback'), token.confirm_code) response = self.client.get(url) record = Record.objects.get(student_id=self.student_id) self.assertEqual(record.state, Record.USED) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data, { 'status': 'success', 'message': 'all correct', })
def test_confirm_success(self): record = Record(student_id=self.student_id) record.state = Record.LOCKED record.save() vote_token = AuthToken.generate(self.student_id, str(self.station.external_id), '70') vote_token.save() url = reverse('confirm') data = {'uid': self.student_id, 'vote_token': vote_token.code, 'token': self.token, 'api_key': settings.API_KEY, 'version': settings.API_VERSION} response = self.client.post(url, data) callback = 'https://{0}{1}?callback={2}'.format( settings.CALLBACK_DOMAIN, reverse('callback'), vote_token.confirm_code) self.assertEqual(response.data, { 'status': 'success', 'ballot': self.authcode.code, 'callback': callback, })
def authenticate(request): # Check parameters internal_id = request.data['cid'] raw_student_id = request.data['uid'] station_id = request.station if settings.ENFORCE_CARD_VALIDATION: # Parse student ID if re.match(r'[A-Z]\d{2}[0-9A-Z]\d{6}', raw_student_id) and re.match(r'[0-9a-f]{8}', internal_id) and re.match(r'\d+', station_id): # Extract parameters student_id = raw_student_id[:-1] revision = int(raw_student_id[-1:]) logger.info('Station %s request for card %s[%s]', station_id, student_id, revision) else: # Malformed card information logger.info('Station %s request for card %s (%s)', station_id, raw_student_id, internal_id) return error('card_invalid') else: # Do not reveal full internal ID as ACA requested logger.info('Station %s request for card (%s****)', station_id, internal_id[:4]) # Call ACA API try: aca_info = service.to_student_id(internal_id) except URLError: logger.exception('Failed to connect to ACA server') return error('external_error', status.HTTP_502_BAD_GATEWAY) except service.ExternalError as e: if not settings.ENFORCE_CARD_VALIDATION: # We can only reveal full internal ID if it’s an invalid card logger.exception('Card rejected by ACA server (%s), reason %s', internal_id, e.reason) else: logger.exception('Card rejected by ACA server, reason %s', e.reason) # Tell clients the exact reason of error if e.reason == 'card_invalid' or e.reason == 'student_not_found': return error('card_invalid') elif e.reason == 'card_blacklisted': return error('card_suspicious') return error('external_error', status.HTTP_502_BAD_GATEWAY) else: if settings.ENFORCE_CARD_VALIDATION: student_id = aca_info.id revision = 0 logger.info('User %s (%s) checked', student_id, aca_info.type) elif aca_info.id != student_id: logger.info('ID %s returned instead', aca_info.id) return error('card_suspicious') # Check vote record try: record = Record.objects.get(student_id=student_id) if settings.ENFORCE_CARD_VALIDATION and record.revision != revision: # ACA claim the card valid! logger.info('Expect revision %s, recorded %s', revision, record.revision) return error('card_suspicious') if record.state == Record.VOTING: # Automaticlly unlock stuck record record.state = Record.AVAILABLE record.save() logger.info('Reset %s state from VOTING', student_id) if record.state != Record.AVAILABLE: logger.error('Duplicate entry (%s)', student_id) return error('duplicate_entry') except Record.DoesNotExist: record = Record(student_id=student_id, revision=revision) # Build up kind identifier try: college = settings.COLLEGE_IDS[aca_info.college] except KeyError: # Use student ID as an alternative logger.warning('No matching college for ACA entry %s', aca_info.college) college = student_id[3] # In rare cases, we may encounter students without colleges if college not in settings.COLLEGE_NAMES: logger.warning('No matching college for ID %s', college) college = '0' # Determine graduate status type_code = student_id[0] kind = college try: override = OverrideEntry.objects.get(student_id=student_id) kind = override.kind except OverrideEntry.DoesNotExist: if type_code in settings.GRADUATE_CODES: if aca_info.department in settings.JOINT_DEPARTMENT_CODES: kind += 'B' else: kind += '1' elif type_code in settings.UNDERGRADUATE_CODES: if aca_info.department in settings.JOINT_DEPARTMENT_CODES: kind += 'A' else: kind += '0' # Check if student has eligible identity if kind not in settings.KINDS: return error('unqualified') # Generate record and token record.state = Record.LOCKED record.save() token = AuthToken.generate(student_id, station_id, kind) token.save() logger.info('Auth token issued: %s', token.code) return Response({'status': 'success', 'uid': student_id, 'type': settings.KINDS[kind], 'vote_token': token.code})
def authenticate(request): # Check parameters internal_id = request.data['cid'] raw_student_id = request.data['uid'] station_id = request.station if settings.ENFORCE_CARD_VALIDATION: # Parse student ID if re.match(r'[A-Z]\d{2}[0-9A-Z]\d{6}', raw_student_id) and re.match(r'[0-9a-f]{8}', internal_id) and re.match(r'\d+', station_id): # Extract parameters student_id = raw_student_id[:-1] revision = int(raw_student_id[-1:]) logger.info('Station %s request for card %s[%s]', station_id, student_id, revision) else: # Malformed card information logger.info('Station %s request for card %s (%s)', station_id, raw_student_id, internal_id) return error('card_invalid') else: # Do not reveal full internal ID as ACA requested logger.info('Station %s request for card (%s****)', station_id, internal_id[:4]) # Call ACA API try: aca_info = service.to_student_id(internal_id) except URLError: logger.exception('Failed to connect to ACA server') return error('external_error', status.HTTP_502_BAD_GATEWAY) except service.ExternalError as e: if not settings.ENFORCE_CARD_VALIDATION: # We can only reveal full internal ID if it’s an invalid card logger.exception('Card rejected by ACA server (%s), reason %s', internal_id, e.reason) else: logger.exception('Card rejected by ACA server, reason %s', e.reason) # Tell clients the exact reason of error if e.reason == 'card_invalid' or e.reason == 'student_not_found': return error('card_invalid') elif e.reason == 'card_blacklisted': return error('card_suspicious') return error('external_error', status.HTTP_502_BAD_GATEWAY) else: if not settings.ENFORCE_CARD_VALIDATION: student_id = aca_info.id revision = 0 logger.info('User %s (%s) checked', student_id, aca_info.type) elif aca_info.id != student_id: logger.info('ID %s returned instead', aca_info.id) return error('card_suspicious') # Check vote record try: record = Record.objects.get(student_id=student_id) if settings.ENFORCE_CARD_VALIDATION and record.revision != revision: # ACA claim the card valid! logger.info('Expect revision %s, recorded %s', revision, record.revision) return error('card_suspicious') if record.state == Record.VOTING: # Automaticlly unlock stuck record record.state = Record.AVAILABLE record.save() logger.info('Reset %s state from VOTING', student_id) if record.state != Record.AVAILABLE: logger.error('Duplicate entry (%s)', student_id) return error('duplicate_entry') except Record.DoesNotExist: record = Record(student_id=student_id, revision=revision) # Determine graduate status kind = kind_classifier.get_kind(aca_info) if kind is None: return error('unqualified') # Generate record and token record.state = Record.LOCKED record.save() token = AuthToken.generate(student_id, station_id, kind) token.save() logger.info('Auth token issued: %s', token.code) return Response({'status': 'success', 'uid': student_id, 'type': aca_info.college, 'college': settings.DPTCODE_NAME[aca_info.department], 'vote_token': token.code})