Пример #1
0
 def custom_stream_factory(total_content_length, filename, content_type, content_length=None):
     if total_content_length > server.config['MAX_CONTENT_LENGTH_EDGELIST']:
         raise exceptions.RequestEntityTooLarge()
     if not allowed_edgelist(filename):
         raise exceptions.ImATeapot()
     tmpfile = NamedTemporaryFile('wb+', delete=False) # delete=False requires manual deletion using os.remove(tmpfile.name)
     return tmpfile
Пример #2
0
    def __init__(self, limit):
        self.limit = limit

        # Set defaults
        self.code = 429
        self.body = self.get_body()
        self.headers = self.get_headers()

        # Get the description
        if limit.error_message:
            self.description = limit.error_message if not callable(
                limit.error_message) else limit.error_message()
        else:
            self.description = text_type(limit.limit)

        # If error is given, get body & headers
        if self.limit.error_code:
            self.code = limit.error_code
            exception = exceptions.HTTPException(description=self.description)

            # Some common error codes, can add more here
            if self.code == 400:
                exception = exceptions.BadRequest()
            elif self.code == 401:
                exception = exceptions.Unauthorized()
            elif self.code == 403:
                exception = exceptions.Forbidden()
            elif self.code == 404:
                exception = exceptions.NotFound()
            elif self.code == 405:
                exception = exceptions.MethodNotAllowed()
            elif self.code == 406:
                exception = exceptions.NotAcceptable()
            elif self.code == 418:
                exception = exceptions.ImATeapot()  # <3
            elif self.code == 500:
                exception = exceptions.InternalServerError()
            elif self.code == 501:
                exception = exceptions.NotImplemented()

            # Update body & headers
            self.body = exception.get_body()
            self.headers = exception.get_headers()
        else:
            exception = exceptions.TooManyRequests(
                description=self.description)

            # Update body & headers
            self.body = exception.get_body()
            self.headers = exception.get_headers()
        super(RateLimitExceeded,
              self).__init__(description=self.description,
                             response=Response(self.body, self.code,
                                               self.headers))
Пример #3
0
def renew_fb():
    '''Renew the current user's Facebook access token.

    The client should make this call periodically (once every couple months,
    see User.should_renew_fb_token) to keep the access token up to date.

    Takes a Facebook signed request object from the post params in the form of:
    {
        'fb_signed_request': obj
    }
    '''
    req = flask.request
    current_user = view_helpers.get_current_user()

    rmclogger.log_event(
        rmclogger.LOG_CATEGORY_API,
        rmclogger.LOG_EVENT_RENEW_FB, {
            'user_id': current_user.id,
            'request_form': req.form,
        }
    )

    fbsr = req.form.get('fb_signed_request')
    if fbsr is None:
        logging.warn('No fbsr set')
        raise exceptions.ImATeapot('No fbsr set')

    fb_data = facebook.get_fb_data(fbsr, app.config)
    access_token = fb_data['access_token']
    expires_on = fb_data['expires_on']
    is_invalid = fb_data['is_invalid']

    if not is_invalid:
        current_user.fb_access_token_expiry_date = expires_on
        current_user.fb_access_token = access_token
        current_user.fb_access_token_invalid = is_invalid

        # Update the user's fb friend list, since it's likely outdated by now
        try:
            current_user.update_fb_friends(
                    facebook.get_friend_list(access_token))
        except:
            # Not sure why this would happen. Usually it's due to invalid
            # access_token, but we JUST got the token, so it should be valid
            logging.warn(
                    "/api/renew-fb: get_friend_list failed with token (%s)"
                    % access_token)

        current_user.save()

    return ''
Пример #4
0
def get_current_user():
    """Get the current user using Flask sessions.

    Also allows admins to become another user based on oid or fbid.

    Returns a User object if the user is logged in, or None otherwise.
    """
    req = flask.request

    if hasattr(req, 'current_user'):
        return req.current_user

    api_key = req.values.get('api_key')
    if api_key and is_api_request():
        req.current_user = m.User.objects(api_key=api_key).first()
        if not req.current_user:
            # TODO(mack): change exceptions to not return html, but just the
            # error text
            raise exceptions.ImATeapot('Invalid api key %s' % api_key)
    elif SESSION_COOKIE_KEY_USER_ID in flask.session:
        user_id = flask.session[SESSION_COOKIE_KEY_USER_ID]
        req.current_user = m.User.objects.with_id(user_id)

        # req.current_user can be None if the client still has a cookie with
        # this user_id, but the corresponding User's account has been deleted.
        if req.current_user:
            req.current_user.update(set__last_visited=datetime.datetime.now())
    else:
        req.current_user = None

    if req.current_user and req.current_user.is_admin:
        oid = req.values.get('as_oid', '')
        fbid = req.values.get('as_fbid', '')
        if oid:
            try:
                as_user = m.User.objects.with_id(oid)
                req.current_user = as_user
                req.as_user_override = True
            except me.base.ValidationError:
                logging.warn("Bad as_oid (%s) in get_current_user()" % oid)
        elif fbid:
            as_user = m.User.objects(fbid=fbid).first()
            if as_user is None:
                logging.warn("Bad as_fbid (%s) in get_current_user()" % fbid)
            else:
                req.current_user = as_user
                req.as_user_override = True

    return req.current_user
Пример #5
0
def login():
    req = flask.request

    fbsr = req.form.get('fb_signed_request')

    # TODO(Sandy): Change log category because this isn't API?
    rmclogger.log_event(
        rmclogger.LOG_CATEGORY_API,
        rmclogger.LOG_EVENT_LOGIN,
        {
            'fbsr': fbsr,
            'request_form': req.form,
        },
    )

    if (fbsr is None):
        raise exceptions.ImATeapot('No fbsr set')

    fb_data = facebook.get_fb_data(fbsr, app.config)
    fbid = fb_data['fbid']
    fb_access_token = fb_data['access_token']
    fb_access_token_expiry_date = fb_data['expires_on']
    is_invalid = fb_data['is_invalid']

    user = m.User.objects(fbid=fbid).first()
    if user:
        # Existing user. Update with latest FB info
        user.fb_access_token = fb_access_token
        user.fb_access_token_expiry_date = fb_access_token_expiry_date
        user.fb_access_token_invalid = is_invalid
        user.save()
        view_helpers.login_as_user(user)

        rmclogger.log_event(
            rmclogger.LOG_CATEGORY_IMPRESSION,
            rmclogger.LOG_EVENT_LOGIN,
            {
                'new_user': False,
                'user_id': user.id,
            },
        )

        return ''

    # Sign up the new user
    friend_fbids = flask.json.loads(req.form.get('friend_fbids'))
    gender = req.form.get('gender')
    first_name = req.form.get('first_name')
    middle_name = req.form.get('middle_name')
    last_name = req.form.get('last_name')
    email = req.form.get('email')

    now = datetime.now()
    user_obj = {
        'fbid': fbid,
        'first_name': first_name,
        'middle_name': middle_name,
        'last_name': last_name,
        'email': email,
        'gender': gender,
        'fb_access_token': fb_access_token,
        'fb_access_token_expiry_date': fb_access_token_expiry_date,
        # TODO(Sandy): Count visits properly
        'join_date': now,
        'join_source': m.User.JoinSource.FACEBOOK,
        'num_visits': 1,
        'last_visited': now,
        'friend_fbids': friend_fbids,
        # TODO(Sandy): Fetch from client side and pass here: name, email,
        # school, program, faculty
    }
    referrer_id = req.form.get('referrer_id')
    if referrer_id:
        try:
            user_obj['referrer_id'] = bson.ObjectId(referrer_id)
        except:
            pass

    user = m.User(**user_obj)
    user.save()
    view_helpers.login_as_user(user)

    rmclogger.log_event(
        rmclogger.LOG_CATEGORY_IMPRESSION,
        rmclogger.LOG_EVENT_LOGIN,
        {
            'new_user': True,
            'user_id': user.id,
            'referrer_id': referrer_id,
        },
    )

    return ''
Пример #6
0
def user_course():
    uc_data = util.json_loads(flask.request.data)
    user = view_helpers.get_current_user()

    rmclogger.log_event(
        rmclogger.LOG_CATEGORY_API,
        rmclogger.LOG_EVENT_USER_COURSE,
        {
            'uc_data': uc_data,
            'user_id': user.id,
        },
    )

    # Validate request object
    course_id = uc_data.get('course_id')
    term_id = uc_data.get('term_id')
    if course_id is None or term_id is None:
        logging.error("/api/user/course got course_id (%s) and term_id (%s)" %
                      (course_id, term_id))
        # TODO(david): Perhaps we should have a request error function that
        # returns a 400
        raise exceptions.ImATeapot('No course_id or term_id set')

    if not m.UserCourse.can_review(term_id):
        logging.warning("%s attempted to rate %s in future/shortlist term %s" %
                        (user.id, course_id, term_id))
        raise exceptions.ImATeapot(
            "Can't review a course in the future or shortlist")

    # Fetch existing UserCourse
    uc = m.UserCourse.objects(user_id=user.id,
                              course_id=uc_data['course_id'],
                              term_id=uc_data['term_id']).first()

    if uc is None:
        logging.error("/api/user/course User course not found for "
                      "user_id=%s course_id=%s term_id=%s" %
                      (user.id, course_id, term_id))
        # TODO(david): Perhaps we should have a request error function that
        # returns a 400
        raise exceptions.ImATeapot('No user course found')

    orig_points = uc.num_points

    # TODO(Sandy): Consider the case where the user picked a professor and
    # rates them, but then changes the professor. We need to remove the ratings
    # from the old prof's aggregated ratings and add them to the new prof's
    # Maybe create professor if newly added
    if uc_data.get('new_prof_added'):

        new_prof_name = uc_data['new_prof_added']

        # TODO(mack): should do guess_names first, and use that to
        # generate the id
        prof_id = m.Professor.get_id_from_name(new_prof_name)
        uc.professor_id = prof_id

        # TODO(Sandy): Have some kind of sanity check for professor names.
        # Don't allow ridiculousness like "Santa Claus", "aksnlf",
        # "swear words"
        if m.Professor.objects(id=prof_id).count() == 0:
            first_name, last_name = m.Professor.guess_names(new_prof_name)
            m.Professor(
                id=prof_id,
                first_name=first_name,
                last_name=last_name,
            ).save()

        course = m.Course.objects.with_id(uc.course_id)
        course.professor_ids = list(set(course.professor_ids) | {prof_id})
        course.save()

        logging.info("Added new course professor %s (name: %s)" %
                     (prof_id, new_prof_name))
    elif uc_data.get('professor_id'):
        uc.professor_id = uc_data['professor_id']
    else:
        uc.professor_id = None

    now = datetime.now()

    if uc_data.get('course_review'):
        # New course review data
        uc_data['course_review']['comment_date'] = now
        uc.course_review.update(**uc_data['course_review'])

    if uc_data.get('professor_review'):
        # New prof review data
        uc_data['professor_review']['comment_date'] = now
        uc.professor_review.update(**uc_data['professor_review'])

    uc.save()

    points_gained = uc.num_points - orig_points
    user.award_points(points_gained, view_helpers.get_redis_instance())
    user.save()

    return util.json_dumps({
        'professor_review.comment_date':
        uc['professor_review']['comment_date'],
        'course_review.comment_date':
        uc['course_review']['comment_date'],
        'points_gained':
        points_gained,
    })
Пример #7
0
def login_with_facebook():
    """Login or create an account using Facebook connect

    Upon successful login or account creation, returns a 'secure cookie'
    (provided by Flask) containing the session data.

    Takes a Facebook signed request in the form of:
    {
        'fb_signed_request': obj
    }
    """
    req = flask.request

    fbsr = req.form.get('fb_signed_request')

    rmclogger.log_event(
        rmclogger.LOG_CATEGORY_GENERIC,
        rmclogger.LOG_EVENT_LOGIN,
        {
            'fbsr': fbsr,
            'request_form': req.form,
            'type': rmclogger.LOGIN_TYPE_STRING_FACEBOOK,
        },
    )

    if (fbsr is None):
        raise exceptions.ImATeapot('No fbsr set')

    fb_data = facebook.get_fb_data(fbsr, app.config)
    fbid = fb_data['fbid']
    fb_access_token = fb_data['access_token']
    fb_access_token_expiry_date = fb_data['expires_on']
    is_invalid = fb_data['is_invalid']

    user = m.User.objects(fbid=fbid).first()
    if user:
        # Existing user. Update with their latest Facebook info
        user.fb_access_token = fb_access_token
        user.fb_access_token_expiry_date = fb_access_token_expiry_date
        user.fb_access_token_invalid = is_invalid
        user.save()

        # Authenticate
        view_helpers.login_as_user(user)

        rmclogger.log_event(
            rmclogger.LOG_CATEGORY_IMPRESSION,
            rmclogger.LOG_EVENT_LOGIN,
            {
                'new_user': False,
                'user_id': user.id,
                'type': rmclogger.LOGIN_TYPE_STRING_FACEBOOK,
            },
        )
    else:
        # New user, or existing email logins user.
        now = datetime.now()
        email = req.form.get('email')
        user_data = {
            'fb_access_token': fb_access_token,
            'fb_access_token_expiry_date': fb_access_token_expiry_date,
            'fbid': fbid,
            'friend_fbids': flask.json.loads(req.form.get('friend_fbids')),
            'gender': req.form.get('gender'),
            'last_visited': now,
        }

        user = m.User.objects(email=email).first() if email else None
        if user:
            # Update existing account with Facebook data
            referrer_id = None
            for k, v in user_data.iteritems():
                user[k] = v
            user.save()
        else:
            # Create an account with their Facebook data
            user_data.update({
                'email': email,
                'first_name': req.form.get('first_name'),
                'join_date': now,
                'join_source': m.User.JoinSource.FACEBOOK,
                'last_name': req.form.get('last_name'),
                'middle_name': req.form.get('middle_name'),
            })

            referrer_id = req.form.get('referrer_id')
            if referrer_id:
                try:
                    user_data['referrer_id'] = bson.ObjectId(referrer_id)
                except bson.errors.InvalidId:
                    pass

            user = m.User(**user_data)
            user.save()

        # Authenticate
        view_helpers.login_as_user(user)

        rmclogger.log_event(
            rmclogger.LOG_CATEGORY_IMPRESSION,
            rmclogger.LOG_EVENT_LOGIN,
            {
                'new_user': True,
                'user_id': user.id,
                'referrer_id': referrer_id,
                'type': rmclogger.LOGIN_TYPE_STRING_FACEBOOK,
            },
        )

    return ''
Пример #8
0
def login_with_facebook():
    req_json = flask.request.get_json()

    fbsr = req_json.get('fb_signed_request')

    if (fbsr is None):
        raise exceptions.ImATeapot('No fbsr set')

    fb_data = facebook.get_fb_data(fbsr, app.config)
    fbid = fb_data['fbid']
    fb_access_token = fb_data['access_token']
    fb_access_token_expiry_date = fb_data['expires_on']
    is_invalid = fb_data['is_invalid']

    user = User.objects(fbid=fbid).first()
    if user:
        # Existing user. Update with their latest Facebook info
        user.fb_access_token = fb_access_token
        user.fb_access_token_expiry_date = fb_access_token_expiry_date
        user.fb_access_token_invalid = is_invalid
        user.save()
    else:
        # New user, or existing email logins user.
        now = datetime.now()
        email = req_json.get('email')
        user_data = {
            'fb_access_token': fb_access_token,
            'fb_access_token_expiry_date': fb_access_token_expiry_date,
            'fbid': fbid,
            'friend_fbids': flask.json.loads(req_json.get('friend_fbids')),
            'gender': req_json.get('gender'),
            'last_visited': now,
        }

        user = User.objects(email=email).first() if email else None
        if user:
            for k, v in user_data.iteritems():
                user[k] = v
            user.save()
        else:
            # Create an account with their Facebook data
            user_data.update({
                'email': email,
                'first_name': req_json.get('first_name'),
                'join_date': now,
                'join_source': User.JoinSource.FACEBOOK,
                'last_name': req_json.get('last_name'),
                'middle_name': req_json.get('middle_name'),
            })

            referrer_id = req_json.get('referrer_id')
            if referrer_id:
                try:
                    user_data['referrer_id'] = bson.ObjectId(referrer_id)
                except bson.errors.InvalidId:
                    pass

            user = User(**user_data)
            user.save()
    if user:
        identity = UserToken(str(user.pk), user.email or '')
        access_token = _jwt.jwt_encode_callback(identity)
        return util.json_dumps({'accessToken': access_token})