Esempio n. 1
0
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)
Esempio n. 2
0
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))
Esempio n. 3
0
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))
Esempio n. 4
0
 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
Esempio n. 5
0
File: lti.py Progetto: ubc/ontask_b
 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 ''
Esempio n. 6
0
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')
Esempio n. 7
0
    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)
Esempio n. 8
0
    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"))
Esempio n. 9
0
    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