Esempio n. 1
0
def set_user_jwt(auth_key, profile):
    """
    Logs in (or signs up) a new user given its JWT and a default profile
    """

    # Load the most current keys from Redis
    jwkeys.import_keyset(redis_db.get('jwkeys'))

    try:
        jwt = JWT(jwt=auth_key, key=jwkeys)
        claims = json_loads(jwt.claims)
    except:
        # This is called if the authorization failed (auth key has been forged)
        # We don't really care about providing malicious requests with debug
        # info so this is just a really basic error message
        return render_error('rejected.'), 403

    # JWT has a couple of fields (claims) that we care about:
    #  - iss: Issuer - Basically provider of open id (google, SE, etc.)
    #  - sub: Subject - Who is auth'd by this.
    # Together we will use these to store an auth method.
    issuer = claims.get('iss')
    subject = claims.get('sub')

    # Error handle against malformed keys. This should never really happen and
    # the error is not shown to the user so doesn't need to be user-friendly
    if not issuer or not subject:
        return render_error('malformed. Missing iss or sub'), 400

    # If we are here that means we have validated a valid login attempt. Now we
    # will delegate to another method
    token = UserJWTToken(identity=subject, issuer=issuer)
    return get_or_set_user(jwt_token=token, profile=profile)
Esempio n. 2
0
def set_user_oauth(code, provider, client_side=False):
    """
    Logs in an OAuth redirect request given the `provider` describing the type
    """

    oauth_provider = oauth_config.get(provider, None)
    if oauth_provider is None:
        return abort(400)

    oauth_callback = oauth_provider.get('token')
    oauth_login = oauth_provider.get('auth')

    oauth_id = oauth_data.get(provider).get('client-id')
    oauth_secret = oauth_data.get(provider).get('client-secret')

    try:
        # Get the auth key. By polling the provider we both validate
        # the login request and now are able to submit requests as
        # users of the provider.
        auth_key = requests.post(oauth_callback,
                                 data={
                                     'code':
                                     code,
                                     'redirect_uri':
                                     canonical_host +
                                     url_for('auth_login_oauth'),
                                     'client_id':
                                     oauth_id,
                                     'client_secret':
                                     oauth_secret
                                 }).json().get('access_token', '')
    except:
        # Errors mean we couldn't get access key
        return render_error('Could not obtain OAuth access token.'), 403

    # If we're client-side we'll stop here
    if client_side:
        return auth_key

    try:
        # Get identity key, this is something that allows us
        # to uniquely identify the user
        oauth_identity, profile = oauth_login(auth_key)
    except:
        # If we get here that means we could not get profile
        # this is our fault since we validated
        return render_error('Could not obtain OAuth profile.'), 500

    # Model instance for the token
    # Links together the provider, provider's ID, and our user ID
    token = UserOAuthToken(provider_id=provider, identity=oauth_identity)

    get_or_set_user(oauth_token=token, profile=profile)
Esempio n. 3
0
def get_following(user_id, page):
    user = User.query.filter_by(id=user_id).first()

    if not isinstance(user, User):
        return render_error('Nonexistent ID'), 404

    if not user.following_public:
        return render_error('Forbidden'), 403

    following = user.following.filter_by().paginate(page, user_list['page_len'], error_out=False)

    return render_json({
        'data': [follower.to_json(current_user=g.user) for follower in following.items],
        'are_more': following.has_next
    })
Esempio n. 4
0
def category_search():
    json = request.get_json(silent=True)

    if not json or 'query' not in json or len(str(json['query'])) == 0:
        return render_error('Bad query')

    return render_json({'results': search_category(query=str(json['query']))})
Esempio n. 5
0
def mark_notifications_status(notifications, status):
    """
    Marks a list of notification IDs as seen. Requires
    authorized user in session
    """

    if not isinstance(g.user, User):
        return render_error('Unauthorized'), 401

    if status == NotificationStatus.SEEN:
        # Basically we won't want "read" messages going to
        # "seen" state
        Notification.query.filter(
            Notification.recipient == g.user,
            Notification.uuid.in_(notifications),
            Notification.read == NotificationStatus.UNSEEN).update(
                {'read': NotificationStatus.SEEN}, synchronize_session=False)
    else:
        Notification.query.filter(Notification.recipient == g.user,
                                  Notification.uuid.in_(notifications)).update(
                                      {'read': status},
                                      synchronize_session=False)

    db.session.commit()
    return render_json({'status': status.value})
Esempio n. 6
0
def auth_login():
    json = request.get_json(silent=True)

    # JSON parsing failed
    if not json or 'token' not in json:
        return render_error('bad json')

    return auth.set_user_jwt(json['token'], json.get('profile'))
Esempio n. 7
0
def get_profile(user_id):
    """
    Returns a user's user_id
    """
    user = User.query.filter_by(id=user_id).first()

    if user is None:
        return render_error('user not found'), 400
    else:
        return render_json(user.to_json())
Esempio n. 8
0
def remove_auth_method(id, user):
    """
    Removes auth method for user
    """
    auth_tokens = UserAuthToken.query.filter_by(user_id=user.id).all()

    if len(auth_tokens) <= 1:
        # You can't remove auth token if you only have 1
        return render_error('not enough tokens'), 412

    selected_token = next(
        (auth_token for auth_token in auth_tokens if auth_token.id == id),
        None)
    if not isinstance(selected_token, UserAuthToken):
        return render_error('invalid method'), 404

    db.session.delete(selected_token)
    db.session.commit()

    return render_json({'id': id})
Esempio n. 9
0
def auth_login_jwt():
    json = request.get_json(silent=True)

    # JSON parsing failed
    if not json or 'token' not in json:
        return render_error('bad json')

    jwt_errors = auth.set_user_jwt(json['token'], json.get('profile'))
    if jwt_errors is not None:
        return jwt_errors
    else:
        return render_json({'user_id': g.user.id})
Esempio n. 10
0
def unfollow(source_user_id, target_user_id):
    """
    Makes 1st param unfollow the 2nd param
    """
    if source_user_id == target_user_id:
        return render_error('cannot follow oneself'), 400

    source_user = User.query.filter_by(id=source_user_id).first()
    target_user = User.query.filter_by(id=target_user_id).first()

    if not isinstance(g.user, User):
        return render_error('Unauthorized'), 401

    if source_user is None or target_user is None:
        return render_error('source user or target user doesn\'t exist'), 400

    if source_user.id != g.user.id:
        return render_error('Forbidden'), 403

    source_user.unfollow(target_user)
    db.session.commit()

    return render_json({ 'following': False })
Esempio n. 11
0
def mark_notification_status(notification_id, status):
    """
    Marks a give notification as read.
    """

    if not isinstance(g.user, User):
        return render_error('Unauthorized'), 401

    notifications = Notification.query.\
        filter_by(recipient_id=g.user.id, uuid=notification_id)

    notifications.update({'read': status})
    db.session.commit()

    return render_json({'status': status.value})
Esempio n. 12
0
def get_or_set_user(jwt_token=None, oauth_token=None, profile={}):
    """
    This will take an auth object and login the user. If the user does not
    exist, it will save (persist) the auth and create a new account using the
    profile details.
    """

    if jwt_token:
        token = UserJWTToken.query.filter_by(identity=jwt_token.identity,
                                             issuer=jwt_token.issuer).first()
    elif oauth_token:
        token = UserOAuthToken.query.filter_by(
            identity=oauth_token.identity,
            provider_id=oauth_token.provider_id).first()
    else:
        return abort(500)

    user = None
    if token is not None:
        # This means the user exists
        user = token.user
    else:
        # This means the user does not exist and we must create it.
        name = profile.get('name')
        email = profile.get('email')

        # Make sure we have both fields or return missing error
        if not name:
            return render_error("Profile does not have name",
                                error_type='bad_profile'), 400

        user = User(name=name, email=email, avatar=profile.get('avatar'))

        if jwt_token:
            user.jwt_tokens.append(jwt_token)
        elif oauth_token:
            user.oauth_tokens.append(oauth_token)

        db.session.add(user)
        db.session.commit()

    user_session.set_session_user(user)
    g.user = user

    ip_address = getattr(request, 'access_route', [request.remote_addr])[0]
    login = Login(ip_address=ip_address, user_id=g.user.id)
    db.session.add(login)
    db.session.commit()
Esempio n. 13
0
def mark_all_notifications_status(status):
    """
    Marks all notifications as seen. Requires
    authorized user
    """

    if not isinstance(g.user, User):
        return render_error('Unauthorized'), 401

    if status == NotificationStatus.SEEN:
        Notification.query.\
            filter_by(recipient=g.user, read=NotificationStatus.UNSEEN).\
            update({'read': status})
    else:
        Notification.query.\
            filter_by(recipient=g.user).\
            update({'read': status})

    db.session.commit()
    return render_json({'status': status.value})
Esempio n. 14
0
def unfollow_user(target_user_id):
    if not isinstance(g.user, User):
        return render_error('Unauthorized'), 401

    return user.unfollow(g.user.id, target_user_id)
Esempio n. 15
0
    def wrap(*args, **kwargs):
        if not isinstance(g.user, User):
            return render_error('Unauthorized'), 401

        return f(*args, **kwargs)
Esempio n. 16
0
def get_or_set_user(auth_token=None, profile={}, auth_opts={}):
    """
    This will take an auth object and login the user. If the user does not
    exist, it will save (persist) the auth and create a new account using the
    profile details.
    """

    if isinstance(g.user, User) and g.user.deleted:
        g.user = None
        return abort(401)

    is_append_flow = auth_opts.get('append', False)

    if auth_token:
        existing_auth_token = UserAuthToken.query.\
            filter_by(
                auth_method=auth_token.auth_method,
                identity=auth_token.identity,
                issuer=auth_token.issuer
            ).first()
    else:
        return abort(500)

    user = None
    if existing_auth_token is not None and existing_auth_token.user is not None:
        # If the login method already belongs to another thing
        if is_append_flow:
            return render_error("Already used method",
                                error_type='duplicate_method'), 403

        # Set to existing auth token
        auth_token = existing_auth_token

        # This means the user exists
        user = existing_auth_token.user

        # make sure existing user isn't deleted
        if user.deleted:
            return abort(401)
    else:
        # If append flow + the user doesn't exist (good), we'll just add the user
        if is_append_flow:
            if not isinstance(g.user, User):
                return render_error("Not authorized",
                                    error_type='unauthorized'), 400

            # Let's add the auth token to current user
            g.user.auth_tokens.append(auth_token)
            db.session.add(auth_token)
            db.session.commit()
            db.session.refresh(auth_token)

            return

        # This means the user does not exist and we must create it.
        name = profile.get('name')
        email = profile.get('email')

        # Make sure we have both fields or return missing error
        if not name:
            return render_error("Profile does not have name",
                                error_type='bad_profile'), 400

        user = User(name=name, email=email, avatar=profile.get('avatar'))

        user.auth_tokens.append(auth_token)

        db.session.add(user)
        db.session.commit()

        # Reload auth token object
        db.session.refresh(auth_token)

    user_session.set_session_user(user)
    g.user = user

    ip_address = getattr(request, 'access_route', [request.remote_addr])[0]
    login = Login(ip_address=ip_address,
                  user_id=g.user.id,
                  login_method_id=auth_token.id)
    db.session.add(login)
    db.session.commit()
Esempio n. 17
0
def set_user_oauth(code, provider, client_side=False, auth_opts={}):
    """
    Logs in an OAuth redirect request given the `provider` describing the type
    """

    oauth_provider = oauth_config.get(provider, None)
    if oauth_provider is None:
        return abort(400)

    oauth_callback = oauth_provider.get('token')
    oauth_login = oauth_provider.get('auth')
    oauth_provider_identifier = provider

    oauth_id = oauth_data.get(oauth_provider_identifier).get('client-id')
    oauth_secret = oauth_data.get(oauth_provider_identifier).get(
        'client-secret')

    try:
        # Get the auth key. By polling the provider we both validate
        # the login request and now are able to submit requests as
        # users of the provider.
        access_token_response = requests.post(oauth_callback,
                                              data={
                                                  'code':
                                                  code,
                                                  'redirect_uri':
                                                  canonical_host +
                                                  url_for('auth_login_oauth'),
                                                  'client_id':
                                                  oauth_id,
                                                  'client_secret':
                                                  oauth_secret,
                                                  'grant_type':
                                                  'authorization_code'
                                              },
                                              headers={
                                                  'Accept': 'application/json'
                                              }).json()
    except Exception as error:
        if bugsnag.configuration.api_key is not None:
            bugsnag.notify(error, meta_data={'oauth': {'provider': provider}})

        # Errors mean we couldn't get access key
        return render_error('Failed to connect to OAuth provider.'), 403

    try:
        auth_key = access_token_response['access_token']
    except Exception as error:
        if bugsnag.configuration.api_key is not None:
            bugsnag.notify(error,
                           meta_data={
                               'oauth': {
                                   'provider': provider,
                                   'data': access_token_response
                               }
                           })

        # Errors mean we couldn't get access key
        return render_error(
            'Could not obtain OAuth access token from OAuth provider.'), 403

    is_append_flow = auth_opts.get('append', False)

    # If we're client-side we'll stop here UNLESS we are appending
    if client_side and not is_append_flow:
        return auth_key

    try:
        # Get identity key, this is something that allows us
        # to uniquely identify the user.
        # The profile['identification'] is a user-readable string.
        oauth_identity, profile = oauth_login(auth_key)
    except Exception as error:
        if bugsnag.configuration.api_key is not None:
            bugsnag.notify(error, meta_data={'oauth': {'provider': provider}})

        # If we get here that means we could not get profile
        # this is our fault since we validated
        return render_error(
            'Could not obtain OAuth profile using provider implementation.'
        ), 500

    if 'identifier' not in profile:
        return render_error('Could not obtain identifier'), 400

    # Model instance for the token
    # Links together the provider, provider's ID, and our user ID
    token = UserAuthToken(auth_method=AuthTokenType.OAUTH,
                          issuer=oauth_provider_identifier,
                          identity=oauth_identity,
                          identifier=profile.get('identifier'))

    return get_or_set_user(auth_token=token,
                           profile=profile,
                           auth_opts=auth_opts)