def notebook_authenticate(cmd_args, force=False, silent=True): """ Similiar to authenticate but prints student emails after all calls and uses a different way to get codes. If SILENT is True, it will suppress the error message and redirect to FORCE=True """ server = server_url(cmd_args) network.check_ssl() access_token = None if not force: try: access_token = refresh_local_token(server) except OAuthException as e: # Account for Invalid Grant Error During make_token_post if not silent: raise e return notebook_authenticate(cmd_args, force=True, silent=False) if not access_token: access_token = perform_oauth(get_code_via_terminal, cmd_args, copy_msg=NOTEBOOK_COPY_MESSAGE, paste_msg=NOTEBOOK_PASTE_MESSAGE) # Always display email email = display_student_email(cmd_args, access_token) if email is None and not force: return notebook_authenticate(cmd_args, force=True) # Token has expired elif email is None: # Did not get a valid token even after a fresh login log.warning('Could not get login email. You may have been logged out. ' ' Try logging in again.') return access_token
def notebook_authenticate(cmd_args, force=False): """ Similiar to authenticate but prints student emails after all calls and uses a different way to get codes. """ server = server_url(cmd_args) network.check_ssl() access_token = None if not force: access_token = refresh_local_token(server) if not access_token: access_token = perform_oauth( get_code_via_terminal, cmd_args, copy_msg=NOTEBOOK_COPY_MESSAGE, paste_msg=NOTEBOOK_PASTE_MESSAGE) # Always display email email = display_student_email(cmd_args, access_token) if email is None and not force: return notebook_authenticate(cmd_args, force=True) # Token has expired elif email is None: # Did not get a valid token even after a fresh login log.warning('Could not get login email. You may have been logged out. ' ' Try logging in again.') return access_token
def authenticate(cmd_args, endpoint='', force=False, nointeract=False): """Returns an OAuth token that can be passed to the server for identification. If FORCE is False, it will attempt to use a cached token or refresh the OAuth token. If NOINTERACT is true, it will return None rather than prompting the user. """ server = server_url(cmd_args) network.check_ssl() access_token = None try: assert not force access_token = refresh_local_token(server) except Exception: if nointeract: return access_token print('Performing authentication') access_token = perform_oauth(get_code, cmd_args, endpoint) email = display_student_email(cmd_args, access_token) if not email: log.warning('Could not get login email. Try logging in again.') log.debug('Authenticated with access token={}'.format(access_token)) return access_token
def authenticate(cmd_args, endpoint='', force=False): """Returns an OAuth token that can be passed to the server for identification. If FORCE is False, it will attempt to use a cached token or refresh the OAuth token. """ server = server_url(cmd_args) network.check_ssl() access_token = None if not force: access_token = refresh_local_token(server) if not access_token: print('Performing authentication') access_token = perform_oauth(get_code, cmd_args, endpoint) email = display_student_email(cmd_args, access_token) if not email: log.warning('Could not get login email. Try logging in again.') return access_token
def run(self, messages, nointeract=False): if not self.assignment.endpoint: log.info('No assignment endpoint, skipping backup') return if self.args.local: print_warning("Cannot backup when running ok with --local.") return if not self.args.insecure: network.check_ssl() if self.args.revise: action = 'revision' elif self.args.submit: action = 'submission' else: action = 'backup' message_list = self.load_unsent_messages() access_token = self.assignment.authenticate(nointeract=nointeract) log.info('Authenticated with access token') log.info('Sending unsent messages') if not access_token: print_error( "Not authenticated. Cannot send {} to server".format(action)) self.dump_unsent_messages(message_list) return # Messages from the current backup to send first is_send_first = self.args.submit or self.args.revise subm_messages = [messages] if is_send_first else [] if is_send_first: response = self.send_all_messages(access_token, subm_messages, current=True) if message_list: self.send_all_messages(access_token, message_list, current=False) else: message_list.append(messages) response = self.send_all_messages(access_token, message_list, current=False) base_url = self.assignment.server_url + '/{}/{}/{}' if isinstance(response, dict): print_success('{action} successful for user: {email}'.format( action=action.title(), email=response['data']['email'])) submission_type = 'submissions' if self.args.submit else 'backups' url = base_url.format(response['data']['assignment'], submission_type, response['data']['key']) if self.args.submit or self.args.backup: print_success('URL: {0}'.format(url)) if self.args.backup: print('NOTE: this is only a backup. ' 'To submit your assignment, use:\n' '\tpython3 ok --submit') self.dump_unsent_messages(message_list + subm_messages) print()
def send_all_messages(self, access_token, message_list, current=False): if not self.args.insecure: ssl = network.check_ssl() else: ssl = None if current and self.args.revise: action = "Revise" elif current and self.args.submit: action = "Submit" else: action = "Backup" num_messages = len(message_list) send_all = self.args.submit or self.args.backup retries = self.RETRY_LIMIT if send_all: timeout = None stop_time = datetime.datetime.max retries = self.RETRY_LIMIT * 2 else: timeout = self.SHORT_TIMEOUT stop_time = datetime.datetime.now() + datetime.timedelta( seconds=timeout) log.info('Setting timeout to %d seconds', timeout) first_response = None error_msg = '' log.info("Sending {0} messages".format(num_messages)) while retries > 0 and message_list and datetime.datetime.now( ) < stop_time: log.info('Sending messages...%d left', len(message_list)) print('{action}... {percent}% complete'.format( action=action, percent=100 - round(len(message_list) * 100 / num_messages, 2)), end='\r') # message_list is assumed to be ordered in chronological order. # We want to send the most recent message first, and send older # messages after. message = message_list[-1] try: response = self.send_messages(access_token, message, timeout, current) except requests.exceptions.Timeout as ex: log.warning("HTTP request timeout: %s", str(ex)) retries -= 1 error_msg = 'Connection timed out after {} seconds. '.format(timeout) + \ 'Please check your network connection.' except (requests.exceptions.RequestException, requests.exceptions.BaseHTTPError) as ex: log.warning('%s: %s', ex.__class__.__name__, str(ex)) retries -= 1 if getattr(ex, 'response', None) is None: error_msg = 'Please check your network connection.' continue try: response_json = ex.response.json() except ValueError as ex: log.warning("Invalid JSON Response", exc_info=True) retries -= 1 error_msg = 'The server did not provide a valid response. Try again soon.' continue log.warning('%s error message: %s', ex.__class__.__name__, response_json['message']) if ex.response.status_code == 401: # UNAUTHORIZED (technically authorization != authentication, but oh well) raise exceptions.AuthenticationException( response_json.get( 'message')) # raise this for the caller elif ex.response.status_code == 403 and 'download_link' in response_json[ 'data']: retries = 0 error_msg = 'Aborting because OK may need to be updated.' else: retries -= 1 error_msg = response_json['message'] except Exception as ex: if ssl and isinstance(ex, ssl.CertificateError): retries = 0 log.warning("SSL Error: %s", str(ex)) error_msg = 'SSL Verification Error: {}\n'.format(ex) + \ 'Please check your network connection and SSL configuration.' else: retries -= 1 log.warning(error_msg, exc_info=True) error_msg = "Unknown Error: {}".format(ex) else: if not first_response: first_response = response message_list.pop() if current and error_msg: print() # Preserve progress bar. print_error('Could not', action.lower() + ':', error_msg) elif not message_list: print('{action}... 100% complete'.format(action=action)) due_date = self.get_due_date(access_token, timeout) if due_date is not None and action != "Revise": now = datetime.datetime.now(tz=datetime.timezone.utc) time_to_deadline = due_date - now if time_to_deadline < datetime.timedelta(0): print_error( "{action} past deadline by".format(action=action), display_timedelta(-time_to_deadline)) elif time_to_deadline < datetime.timedelta(hours=10): print_warning("Assignment is due in", display_timedelta(time_to_deadline)) return first_response elif not send_all: # Do not display any error messages if --backup or --submit are not # used. print() elif not error_msg: # No errors occurred, but could not complete request within TIMEOUT. print() # Preserve progress bar. print_error('Could not {} within {} seconds.'.format( action.lower(), timeout)) else: # If not all messages could be backed up successfully. print() # Preserve progress bar. print_error('Could not', action.lower() + ':', error_msg)
def run(self, messages): if not self.assignment.endpoint: log.info('No assignment endpoint, skipping backup') return if self.args.local: print("Cannot backup when running ok with --local.") return if not self.args.insecure: network.check_ssl() if self.args.revise: action = 'revision' elif self.args.submit: action = 'submission' else: action = 'backup' message_list = self.load_unsent_messages() access_token = self.assignment.authenticate() log.info('Authenticated with access token') log.info('Sending unsent messages') if not access_token: print("Not authenticated. Cannot send {} to server".format(action)) self.dump_unsent_messages(message_list) return # Messages from the current backup to send first is_send_first = self.args.submit or self.args.revise subm_messages = [messages] if is_send_first else [] if is_send_first: response = self.send_all_messages(access_token, subm_messages, current=True) if message_list: self.send_all_messages(access_token, message_list, current=False) else: message_list.append(messages) response = self.send_all_messages(access_token, message_list, current=False) base_url = self.assignment.server_url + '/{}/{}/{}' if isinstance(response, dict): print('{action} successful for user: {email}'.format(action=action.title(), email=response['data']['email'])) submission_type = 'submissions' if self.args.submit else 'backups' url = base_url.format(response['data']['assignment'], submission_type, response['data']['key']) if self.args.submit or self.args.backup: print('URL: {0}'.format(url)) if self.args.backup: print('NOTE: this is only a backup. ' 'To submit your assignment, use:\n' '\tpython3 ok --submit') self.dump_unsent_messages(message_list + subm_messages) print()
def send_all_messages(self, access_token, message_list, current=False): if not self.args.insecure: ssl = network.check_ssl() else: ssl = None if current and self.args.revise: action = "Revise" elif current and self.args.submit: action = "Submit" else: action = "Backup" num_messages = len(message_list) send_all = self.args.submit or self.args.backup retries = self.RETRY_LIMIT if send_all: timeout = None stop_time = datetime.datetime.max retries = self.RETRY_LIMIT * 2 else: timeout = self.SHORT_TIMEOUT stop_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout) log.info('Setting timeout to %d seconds', timeout) first_response = None error_msg = '' log.info("Sending {0} messages".format(num_messages)) while retries > 0 and message_list and datetime.datetime.now() < stop_time: log.info('Sending messages...%d left', len(message_list)) print('{action}... {percent}% complete'.format(action=action, percent=100 - round(len(message_list) * 100 / num_messages, 2)), end='\r') # message_list is assumed to be ordered in chronological order. # We want to send the most recent message first, and send older # messages after. message = message_list[-1] try: response = self.send_messages(access_token, message, timeout, current) except requests.exceptions.Timeout as ex: log.warning("HTTP request timeout: %s", str(ex)) retries -= 1 error_msg = 'Connection timed out after {} seconds. '.format(timeout) + \ 'Please check your network connection.' except (requests.exceptions.RequestException, requests.exceptions.BaseHTTPError) as ex: log.warning('%s: %s', ex.__class__.__name__, str(ex)) retries -= 1 if getattr(ex, 'response', None) is None: error_msg = 'Please check your network connection.' continue try: response_json = ex.response.json() except ValueError as ex: log.warning("Invalid JSON Response", exc_info=True) retries -= 1 error_msg = 'The server did not provide a valid response. Try again soon.' continue log.warning('%s error message: %s', ex.__class__.__name__, response_json['message']) if ex.response.status_code == 403 and 'download_link' in response_json['data']: retries = 0 error_msg = 'Aborting because OK may need to be updated.' else: retries -= 1 error_msg = response_json['message'] except Exception as ex: if ssl and isinstance(ex, ssl.CertificateError): retries = 0 log.warning("SSL Error: %s", str(ex)) error_msg = 'SSL Verification Error: {}\n'.format(ex) + \ 'Please check your network connection and SSL configuration.' else: retries -= 1 log.warning(error_msg, exc_info=True) error_msg = "Unknown Error: {}".format(ex) else: if not first_response: first_response = response message_list.pop() if current and error_msg: print() # Preserve progress bar. print('Could not', action.lower() + ':', error_msg) elif not message_list: print('{action}... 100% complete'.format(action=action)) return first_response elif not send_all: # Do not display any error messages if --backup or --submit are not # used. print() elif not error_msg: # No errors occurred, but could not complete request within TIMEOUT. print() # Preserve progress bar. print('Could not {} within {} seconds.'.format(action.lower(), timeout)) else: # If not all messages could be backed up successfully. print() # Preserve progress bar. print('Could not', action.lower() + ':', error_msg)