def load_table( table_name: str, columns: Optional[List[str]] = None, filter_exp: Optional[Dict] = None, ) -> Optional[pd.DataFrame]: """Load a Pandas data frame from the SQL DB. :param table_name: Table name :param columns: Optional list of columns to load (all if NOne is given) :param filter_exp: JSON expression to filter a subset of rows :return: data frame """ if table_name not in connection.introspection.table_names(): return None if settings.DEBUG: LOGGER.debug('Loading table %s', table_name) if columns or filter_exp: # A list of columns or a filter exp is given query, query_fields = sql.get_select_query_txt( table_name, column_names=columns, filter_formula=filter_exp) return pd.read_sql_query(query, OnTaskSharedState.engine, params=query_fields) # No special fields given, load the whole thing return pd.read_sql_table(table_name, OnTaskSharedState.engine)
def create_ontaskuser_handler(sender, **kwargs): """Create the user extensions whenever a new user is created.""" del sender created = kwargs.get('created', True) instance = kwargs.get('instance') if not created or not instance: return # Create the profile and ontask user objects, only if it is newly created ouser = models.OnTaskUser(user=instance) ouser.save() profile = models.Profile(user=instance) profile.save() LOGGER.info(_('New ontask user profile for %s created'), str(instance))
def delete_table(table_name: str): """Delete the given table. :param table_name: Table to delete :return: Drop the table in the DB """ query = sql.SQL('DROP TABLE IF EXISTS {0}').format( sql.Identifier(table_name)) try: with connection.connection.cursor() as cursor: cursor.execute(query) except Exception as exc: LOGGER.error('Error when dropping table %s: %s', table_name, str(exc))
def clean_username(self, username, request): """ Allows the backend to clean the username, if the backend defines a clean_username method. """ backend_str = request.session[auth.BACKEND_SESSION_KEY] backend = auth.load_backend(backend_str) try: LOGGER.debug('calling the backend %s clean_username with %s', backend, username) username = backend.clean_username(username) LOGGER.debug('cleaned username is %s', username) except AttributeError: # Backend has no clean_username method. pass return username
def render(self, context): request = get_current_request() if request: rli = request.LTI.get('resource_link_id') if rli: return format_html( "<input type='hidden' name='resource_link_id' value='{}' />", rli) else: LOGGER.warning( _('Failed to find resource_link_id ' + 'in request context %s'), json.dumps(request.LTI, indent=4)) else: LOGGER.warning( _("Failed to get current request from thread_local")) return ''
def create_db_engine( dialect: str, driver: str, username: str, password: str, host: str, dbname: str, ): """Create SQLAlchemy DB Engine to connect Pandas <-> DB. Function that creates the engine object to connect to the database. The object is required by the pandas functions to_sql and from_sql :param dialect: Dialect for the engine (oracle, mysql, postgresql, etc) :param driver: DBAPI driver (psycopg2, ...) :param username: Username to connect with the database :param password: Password to connect with the database :param host: Host to connect with the database :param dbname: database name :return: the engine """ database_url = '{dial}{drv}://{usr}:{pwd}@{h}/{dbname}'.format( dial=dialect, drv=driver, usr=username, pwd=password, h=host, dbname=dbname, ) if settings.DEBUG: LOGGER.debug('Creating engine: %s', database_url) return sqlalchemy.create_engine(database_url, client_encoding=str('utf8'), encoding=str('utf8'), echo=False, paramstyle='format')
def process_request(self, request): if settings.DEBUG: LOGGER.debug('inside process_request %s', request.path) # AuthenticationMiddleware is required so that request.user exists. if not getattr(request, 'user', None): LOGGER.debug('improperly configured: request has no user attr') raise ImproperlyConfigured( "The Django LTI auth middleware requires the" " authentication middleware to be installed. Edit your" " MIDDLEWARE_CLASSES setting to insert" " 'django.contrib.auth.middleware.AuthenticationMiddleware'" " before the PINAuthMiddleware class.") resource_link_id = None if request.method == 'POST' and request.POST.get( 'lti_message_type') == 'basic-lti-launch-request': LOGGER.debug( 'received a basic-lti-launch-request - authenticating the user' ) # authenticate and log the user in with Timer() as t: user = auth.authenticate(request=request) LOGGER.debug('authenticate() took %s s', t.secs) if user is not None: # User is valid. Set request.user and persist user in the # session by logging the user in. LOGGER.debug( 'user was successfully authenticated; now log them in') request.user = user with Timer() as t: auth.login(request, user) LOGGER.debug('login() took %s s', t.secs) resource_link_id = request.POST.get('resource_link_id') lti_launch = { 'context_id': request.POST.get('context_id'), 'context_label': request.POST.get('context_label'), 'context_title': request.POST.get('context_title'), 'context_type': request.POST.get('context_type'), 'custom_canvas_account_id': request.POST.get('custom_canvas_account_id'), 'custom_canvas_account_sis_id': request.POST.get('custom_canvas_account_sis_id'), 'custom_canvas_api_domain': request.POST.get('custom_canvas_api_domain'), 'custom_canvas_course_id': request.POST.get('custom_canvas_course_id'), 'custom_canvas_enrollment_state': request.POST.get('custom_canvas_enrollment_state'), 'custom_canvas_membership_roles': request.POST.get('custom_canvas_membership_roles', '').split(','), 'custom_canvas_user_id': request.POST.get('custom_canvas_user_id'), 'custom_canvas_user_login_id': request.POST.get('custom_canvas_user_login_id'), 'launch_presentation_css_url': request.POST.get('launch_presentation_css_url'), 'launch_presentation_document_target': request.POST.get('launch_presentation_document_target'), 'launch_presentation_height': request.POST.get('launch_presentation_height'), 'launch_presentation_locale': request.POST.get('launch_presentation_locale'), 'launch_presentation_return_url': request.POST.get('launch_presentation_return_url'), 'launch_presentation_width': request.POST.get('launch_presentation_width'), 'lis_course_offering_sourcedid': request.POST.get('lis_course_offering_sourcedid'), 'lis_outcome_service_url': request.POST.get('lis_outcome_service_url'), 'lis_person_contact_email_primary': request.POST.get('lis_person_contact_email_primary'), 'lis_person_name_family': request.POST.get('lis_person_name_family'), 'lis_person_name_full': request.POST.get('lis_person_name_full'), 'lis_person_name_given': request.POST.get('lis_person_name_given'), 'lis_person_sourcedid': request.POST.get('lis_person_sourcedid'), 'lti_message_type': request.POST.get('lti_message_type'), 'resource_link_description': request.POST.get('resource_link_description'), 'resource_link_id': resource_link_id, 'resource_link_title': request.POST.get('resource_link_title'), 'roles': request.POST.get('roles', '').split(','), 'selection_directive': request.POST.get('selection_directive'), 'tool_consumer_info_product_family_code': request.POST.get('tool_consumer_info_product_family_code'), 'tool_consumer_info_version': request.POST.get('tool_consumer_info_version'), 'tool_consumer_instance_contact_email': request.POST.get('tool_consumer_instance_contact_email'), 'tool_consumer_instance_description': request.POST.get('tool_consumer_instance_description'), 'tool_consumer_instance_guid': request.POST.get('tool_consumer_instance_guid'), 'tool_consumer_instance_name': request.POST.get('tool_consumer_instance_name'), 'tool_consumer_instance_url': request.POST.get('tool_consumer_instance_url'), 'user_id': request.POST.get('user_id'), 'user_image': request.POST.get('user_image'), } # If a custom role key is defined in project, merge into # existing role list if getattr(settings, 'LTI_CUSTOM_ROLE_KEY', None): custom_roles = request.POST.get( settings.LTI_CUSTOM_ROLE_KEY, '').split(',') lti_launch['roles'] += [_f for _f in custom_roles if _f ] # Filter out any empty roles lti_launches = request.session.get('LTI_LAUNCH') if not lti_launches or not isinstance(lti_launches, OrderedDict): lti_launches = OrderedDict() request.session['LTI_LAUNCH'] = lti_launches # Limit the number of LTI launches stored in the session max_launches = getattr(settings, 'LTI_AUTH_MAX_LAUNCHES', 10) LOGGER.info("LTI launch count %s [max=%s]", len(list(lti_launches.keys())), max_launches) if len(list(lti_launches.keys())) >= max_launches: invalidated_launch = lti_launches.popitem(last=False) LOGGER.info("LTI launch invalidated: %s", json.dumps(invalidated_launch, indent=4)) lti_launches[resource_link_id] = lti_launch LOGGER.info("LTI launch added to session: %s", json.dumps(lti_launch, indent=4)) else: # User could not be authenticated! LOGGER.warning('user could not be authenticated via LTI ' 'params; let the request continue in case ' 'another auth plugin is configured') else: resource_link_id = request.GET.get('resource_link_id') setattr( request, 'LTI', request.session.get('LTI_LAUNCH', {}).get(resource_link_id, {})) set_current_request(request) if not request.LTI and settings.DEBUG: LOGGER.warning("Could not find LTI launch for resource_link_id %s", resource_link_id)
def process_request(self, request): if settings.DEBUG: LOGGER.debug('inside process_request %s' % request.path) # AuthenticationMiddleware is required so that request.user exists. if not getattr(request, 'user', None): LOGGER.debug(_('improperly configured: requeset has no user attr')) raise ImproperlyConfigured( _("The Django LTI auth middleware requires the" " authentication middleware to be installed. Edit your" " MIDDLEWARE_CLASSES setting to insert" " 'django.contrib.auth.middleware.AuthenticationMiddleware'" " before the PINAuthMiddleware class.")) if request.method == 'POST' and request.POST.get( 'lti_message_type') == 'basic-lti-launch-request': LOGGER.debug( _('received a basic-lti-launch-request - ' 'authenticating the user')) # authenticate and log the user in with Timer() as t: user = auth.authenticate(request=request) LOGGER.debug(_('authenticate() took %s s') % t.secs) if user is not None: # User is valid. Set request.user and persist user in the session # by logging the user in. LOGGER.debug( _('user was successfully authenticated; now log them in')) request.user = user with Timer() as t: auth.login(request, user) LOGGER.debug('login() took %s s' % t.secs) lti_launch = { 'context_id': request.POST.get('context_id'), 'context_label': request.POST.get('context_label'), 'context_title': request.POST.get('context_title'), 'context_type': request.POST.get('context_type'), 'custom_canvas_account_id': request.POST.get('custom_canvas_account_id'), 'custom_canvas_account_sis_id': request.POST.get('custom_canvas_account_sis_id'), 'custom_canvas_api_domain': request.POST.get('custom_canvas_api_domain'), 'custom_canvas_course_id': request.POST.get('custom_canvas_course_id'), 'custom_canvas_membership_roles': request.POST.get('custom_canvas_membership_roles', '').split(','), 'custom_canvas_enrollment_state': request.POST.get('custom_canvas_enrollment_state'), 'custom_canvas_user_id': request.POST.get('custom_canvas_user_id'), 'custom_canvas_user_login_id': request.POST.get('custom_canvas_user_login_id'), 'launch_presentation_css_url': request.POST.get('launch_presentation_css_url'), 'launch_presentation_document_target': request.POST.get('launch_presentation_document_target'), 'launch_presentation_height': request.POST.get('launch_presentation_height'), 'launch_presentation_locale': request.POST.get('launch_presentation_locale'), 'launch_presentation_return_url': request.POST.get('launch_presentation_return_url'), 'launch_presentation_width': request.POST.get('launch_presentation_width'), 'lis_course_offering_sourcedid': request.POST.get('lis_course_offering_sourcedid'), 'lis_outcome_service_url': request.POST.get('lis_outcome_service_url'), 'lis_person_contact_email_primary': request.POST.get('lis_person_contact_email_primary'), 'lis_person_name_family': request.POST.get('lis_person_name_family'), 'lis_person_name_full': request.POST.get('lis_person_name_full'), 'lis_person_name_given': request.POST.get('lis_person_name_given'), 'lis_person_sourcedid': request.POST.get('lis_person_sourcedid'), 'lti_message_type': request.POST.get('lti_message_type'), 'resource_link_description': request.POST.get('resource_link_description'), 'resource_link_id': request.POST.get('resource_link_id'), 'resource_link_title': request.POST.get('resource_link_title'), 'roles': request.POST.get('roles', '').split(','), 'selection_directive': request.POST.get('selection_directive'), 'tool_consumer_info_product_family_code': request.POST.get('tool_consumer_info_product_family_code'), 'tool_consumer_info_version': request.POST.get('tool_consumer_info_version'), 'tool_consumer_instance_contact_email': request.POST.get('tool_consumer_instance_contact_email'), 'tool_consumer_instance_description': request.POST.get('tool_consumer_instance_description'), 'tool_consumer_instance_guid': request.POST.get('tool_consumer_instance_guid'), 'tool_consumer_instance_name': request.POST.get('tool_consumer_instance_name'), 'tool_consumer_instance_url': request.POST.get('tool_consumer_instance_url'), 'user_id': request.POST.get('user_id'), 'user_image': request.POST.get('user_image'), } # If a custom role key is defined in project, merge into existing role list if getattr(settings, 'LTI_CUSTOM_ROLE_KEY', None): custom_roles = request.POST.get( settings.LTI_CUSTOM_ROLE_KEY, '').split(',') # Filter out any empty roles lti_launch['roles'] += [_f for _f in custom_roles if _f] request.session['LTI_LAUNCH'] = lti_launch else: # User could not be authenticated! LOGGER.warning( _('user could not be authenticated via LTI params; let ' 'the request continue in case another auth plugin is ' 'configured')) # Other functions in django-auth-lti expect there to be an LTI attribute on the request object # This enables backwards compatibility with consumers of this package who still want to use this # single launch version of LTIAuthMiddleware setattr(request, 'LTI', request.session.get('LTI_LAUNCH', {})) if not request.LTI and settings.DEBUG: LOGGER.warning(_("Could not find LTI launch parameters"))
def authenticate( self, request: HttpRequest, username: Optional[str] = None, password: Optional[str] = None, **kwargs: Mapping, ): """Try to authenticate an LTI request.""" if settings.DEBUG: LOGGER.info('Begin authentication process') if not request: if settings.DEBUG: LOGGER.error('No request object in authentication') return None request_key = request.POST.get('oauth_consumer_key') if request_key is None: LOGGER.error( 'Request does not contain an oauth_consumer_key. Stopping') return None if not settings.LTI_OAUTH_CREDENTIALS: LOGGER.error('Missing LTI_OAUTH_CREDENTIALS in settings') raise PermissionDenied secret = settings.LTI_OAUTH_CREDENTIALS.get(request_key) if secret is None: LOGGER.error('Could not get a secret for key %s', request_key) raise PermissionDenied LOGGER.debug('using key/secret %s/%s', request_key, secret) tool_provider = DjangoToolProvider( request_key, secret, request.POST.dict()) postparams = request.POST.dict() LOGGER.debug('Request is secure: %s', request.is_secure()) if settings.DEBUG: for key in postparams: LOGGER.debug('POST %s: %s', key, postparams.get(key)) LOGGER.debug('Request abs url is %s', request.build_absolute_uri()) for key in request.META: LOGGER.debug('META %s: %s', key, request.META.get(key)) LOGGER.info('Checking the signature') try: request_is_valid = tool_provider.is_valid_request(request) except oauth2.Error: LOGGER.exception( 'error attempting to validate LTI launch %s', postparams) request_is_valid = False if not request_is_valid: LOGGER.error('Invalid request: signature check failed.') raise PermissionDenied LOGGER.info('done checking the signature') LOGGER.info( 'about to check the timestamp: {%s}', int(tool_provider.oauth_timestamp)) if time() - int(tool_provider.oauth_timestamp) > 60 * 60: LOGGER.error('OAuth timestamp is too old.') # raise PermissionDenied else: LOGGER.info('Valid timestamp') LOGGER.info('Done checking the timestamp') # (this is where we should check the nonce) # if we got this far, the user is good user = None # Retrieve username from LTI parameter or default to an overridable # function return value username = ( tool_provider.lis_person_sourcedid or self.get_default_username( tool_provider, prefix=self.unknown_user_prefix)) email = tool_provider.lis_person_contact_email_primary first_name = tool_provider.lis_person_name_given last_name = tool_provider.lis_person_name_family roles = tool_provider.roles # Check that we have an email field at least if not email: LOGGER.error('Invalid request: Invalid email.') raise PermissionDenied LOGGER.info('Valid username: %s', username) user_model = get_user_model() # Note that this could be accomplished in one try-except clause, but # instead we use get_or_create when creating unknown users since it has # built-in safeguards for multiple threads. if self.create_unknown_user: user, created = user_model.objects.get_or_create(email=email) if created: LOGGER.debug( 'Authenticate created a new user for %s', username) else: LOGGER.debug( 'Authenticate found an existing user for %s', username) else: LOGGER.debug( 'automatic user creation disbled. Find and existing record') try: user = user_model.objects.get_by_natural_key(username) except user_model.DoesNotExist: LOGGER.debug('authenticate could not find user %s', username) # update user information if given by LTI and not present in user obj. if not user.name and username: user.name = username if not user.name and first_name and last_name: user.name = first_name + ' ' + last_name # check if substring group_role in the user's launch roles should_be_in_instructor_group = any( group_role_substring in roles for group_role_substring in settings.LTI_INSTRUCTOR_GROUP_ROLES ) if ( should_be_in_instructor_group and not user.groups.filter(name='instructor').exists() ): user.groups.add(Group.objects.get(name='instructor')) user.save() LOGGER.debug('Updated the user record in the database') return user