Пример #1
0
def init_app_without_routes(disable_csrf=False):
    app = Flask(__name__)

    app.secret_key = config.get_config()['sessions_secret']

    app.config['SQLALCHEMY_DATABASE_URI'] = config.get_config(
    )['postgres']['url']

    if config.get_config()['postgres'].get('commercial_url'):
        app.config['SQLALCHEMY_BINDS'] = {
            'commercial': config.get_config()['postgres']['commercial_url']
        }

    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

    global db

    class SQLAlchemy(_BaseSQLAlchemy):
        def apply_pool_defaults(self, app, options):
            super(SQLAlchemy, self).apply_pool_defaults(app, options)
            options["pool_pre_ping"] = True

    db = SQLAlchemy(app)

    from modules.base import authentication

    if os.getenv('ENVIRONMENT') == 'test_env':
        app.before_request(authentication.login_test_user)

    @app.errorhandler(403)
    def handle_403(error):
        return jsonify({
            'error_type': 'error_bar',
            'error': error.description or ''
        }), 403

    login_manager = LoginManager()
    login_manager.init_app(app)

    global csrf_protect

    if not disable_csrf:
        csrf_protect = CSRFProtect()
        csrf_protect.init_app(app)

    @login_manager.user_loader
    def load_user(user_id):
        from modules.users.helpers import get_user_by_id

        sentry_sdk.set_user({'id': user_id})

        return get_user_by_id(user_id)

    app.before_request(authentication.validate_user_authentication)

    @app.route('/_/health_check')
    def health_check():
        return 'OK'

    return app
Пример #2
0
def init_app_without_routes(disable_csrf=False):
    app = Flask(__name__)

    app.secret_key = get_config()['sessions_secret']

    app.config['SQLALCHEMY_DATABASE_URI'] = get_config()['postgres']['url']

    if os.getenv('POSTGRES_URL_COMMERCIAL'):
        app.config['SQLALCHEMY_BINDS'] = {
            'commercial': os.getenv('POSTGRES_URL_COMMERCIAL')
        }

    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

    global db
    global migrate

    class SQLAlchemy(_BaseSQLAlchemy):
        def apply_pool_defaults(self, app, options):
            super(SQLAlchemy, self).apply_pool_defaults(app, options)
            options["pool_pre_ping"] = True

    db = SQLAlchemy(app)
    migrate = Migrate(app, db)

    if os.getenv('ENVIRONMENT') == 'test_env':
        from modules.base.authentication import login_test_user

        app.before_request(login_test_user)

    @app.errorhandler(403)
    def handle_403(error):
        return jsonify({
            'error_type': 'error_bar',
            'error': error.description or ''
        }), 403

    login_manager = LoginManager()
    login_manager.init_app(app)

    if not disable_csrf:
        csrf_protect = CSRFProtect()
        csrf_protect.init_app(app)

    @login_manager.user_loader
    def load_user(user_id):
        from modules.users.helpers import get_user_by_id

        return get_user_by_id(user_id)

    return app
Пример #3
0
def login_via_test_token():
  # used only for end-to-end tests
  if not request.args.get('test_token'):
    return False

  payload = jwt.decode(request.args.get('test_token'), get_config()['testing']['secret'], 'HS256')

  if payload['user_email'].split('@')[1] not in get_config()['testing']['domains']:
    raise Exception('Invalid test user %s, with test token: %s' % (payload['user_email'],
                                                                   request.args.get('test_token')))

  authentication.login('test_token', user_email=payload['user_email'])

  return True
Пример #4
0
def login_with_jwt():
  token = request.args.get('token')

  if not token:
    return abort(400)

  try:
    user_info = jwt.decode(token, get_config()['sessions_secret'], algorithms=['HS256'])

    if 'id' in user_info:
      authentication.login(user_info['method'], user_id=user_info['id'])
    else:
      if get_organization_id_for_email(user_info['email']) != user_info['organization']:
        logging.warning('Attempt to use JWT with mismatched org: %s', token)

        return abort(400)

      authentication.login(user_info['method'], user_email=user_info['email'])
  except jwt.DecodeError:
    logging.warning('Attempt to use invalid JWT: %s', token)

    return abort(400)
  except jwt.ExpiredSignatureError:
    logging.warning('Attempt to use expired JWT: %s', token)

  return redirect('/')
Пример #5
0
def create_transfer_link(link_id):
    TOKEN_DURATION_IN_HOURS = 24

    payload = {
        'exp':
        datetime.datetime.utcnow() +
        datetime.timedelta(hours=TOKEN_DURATION_IN_HOURS),
        'sub':
        f'link:{link_id}',
        'tp':
        'transfer',  # "tp" -> token permissions
        'o':
        user_helpers.get_or_create_user(
            g.link.owner, g.link.organization).id,  # "o" -> link owner
        'by':
        current_user.id
    }

    token = jwt.encode(payload,
                       config.get_config()['sessions_secret'],
                       algorithm='HS256')

    full_url = f"{request.host_url}_transfer/{base64.urlsafe_b64encode(token).decode('utf-8').strip('=')}"

    return jsonify({'url': full_url}), 201
Пример #6
0
  def test_get_config__config_from_app_yml_file(self, _0, mock_is_file, _2):
    with patch('builtins.open', mock_open(read_data='')) as mocked_open:
      self.assertEqual({'mock': 'config'},
                       config.get_config())

      self.assertTrue(mocked_open.call_args[0][0].endswith('app.yml'))
      self.assertEqual(1, len(mock_is_file.call_args_list))
      self.assertTrue(mock_is_file.call_args[0][0].endswith('secrets.yaml'))
Пример #7
0
def use_transfer_link(transfer_link_token):
  user_facing_error = None

  try:
    padded_token = transfer_link_token + '=' * (4 - len(transfer_link_token) % 4)  # add any missing base64 padding

    payload = jwt.decode(base64.urlsafe_b64decode(padded_token),
                         config.get_config()['sessions_secret'],
                         'HS256')
  except (jwt.exceptions.ExpiredSignatureError,
          jwt.exceptions.InvalidSignatureError,
          jwt.exceptions.DecodeError) as e:
    if type(e) is jwt.exceptions.ExpiredSignatureError:
      user_facing_error = 'Your transfer link has expired'

      logging.info('Attempt to use expired token: %s', transfer_link_token)
    if type(e) is jwt.exceptions.InvalidSignatureError:
      logging.warning('Attempt to use invalid token: %s', transfer_link_token)

    abort(403, user_facing_error or 'Your transfer link is no longer valid')

  try:
    if not payload['sub'].startswith('link:'):
      raise InvalidTransferToken('Subject is not link')

    if 'transfer' != payload['tp']:
      raise InvalidTransferToken('Invalid token permission')

    link_id = int(payload['sub'][len('link:'):])
    link = models.ShortLink.get_by_id(link_id)
    if not link:
      raise InvalidTransferToken('Link does not exist')

    owner_from_token = user_helpers.get_user_by_id(payload['o'])
    if not owner_from_token or link.owner != owner_from_token.email:
      user_facing_error = f'The owner of rfg/{link.shortpath} has changed since your transfer link was created'

      raise InvalidTransferToken('Owner from token does not match current owner')

    if not check_mutate_authorization(link_id, payload['by']):
      user_facing_error = f'The user who created your transfer link no longer has edit rights for rfg/{link.shortpath}'

      raise InvalidTransferToken('Token from unauthorized user')

    if current_user.organization != link.organization:
      raise InvalidTransferToken("Current user does not match link's organization")
  except (InvalidTransferToken,
          KeyError) as e:
    logging.warning(e)
    logging.warning('Attempt to use invalid token: %s', transfer_link_token)

    abort(403, user_facing_error or 'Your transfer link is no longer valid')

  link.owner = current_user.email
  link.put()

  return '', 201
Пример #8
0
  def test_get_config__config_from_single_env_var(self, _):
    def get_env(var_name):
      return {'TROTTO_CONFIG': 'YTogMQpiOiAy',
              'DATABASE_URL': 'pg_url',
              'FLASK_SECRET': 'the_secret'
              }[var_name]

    with patch('os.getenv', side_effect=get_env):
      self.assertEqual({'a': 1, 'b': 2},
                       config.get_config())
Пример #9
0
  def test_get_config__config_from_multiple_env_vars(self, _):
    def get_env(var_name):
      return {'TROTTO_CONFIG': None,
              'DATABASE_URL': 'pg_url',
              'FLASK_SECRET': 'the_secret'
              }[var_name]

    with patch('os.getenv', side_effect=get_env):
      self.assertEqual({'postgres': {'url': 'pg_url'},
                        'sessions_secret': 'the_secret'},
                       config.get_config())
Пример #10
0
def _deliver_event(event_id, event_type, timestamp, object_type, object_data):
    object_data['object'] = object_type

    event_object = {
        'id': event_id,
        'type': event_type,
        'created': timestamp,
        'data': {
            'object': object_data
        }
    }

    for url in config.get_config().get('event_subscribers', []):
        defer(_deliver_event_to_url, url, event_object, _queue='events')
Пример #11
0
def init_app_without_routes(disable_csrf=False):
  app = Flask(__name__)

  app.secret_key = get_config()['sessions_secret']

  if get_database() == 'postgres':
    app.config['SQLALCHEMY_DATABASE_URI'] = get_config()['postgres']['url']
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

    global db
    global migrate
    db = SQLAlchemy(app)
    migrate = Migrate(app, db)

  if os.getenv('ENVIRONMENT') == 'test_env':
    from modules.base.authentication import login_test_user

    app.before_request(login_test_user)

  @app.errorhandler(403)
  def handle_403(error):
    return jsonify({'error_type': 'error_bar',
                    'error': error.description or ''
                    }), 403

  login_manager = LoginManager()
  login_manager.init_app(app)

  if not disable_csrf:
    csrf_protect = CSRFProtect()
    csrf_protect.init_app(app)

  @login_manager.user_loader
  def load_user(user_id):
    return get_user_by_id(user_id)

  return app
Пример #12
0
def layout_config():
    layout_json = json.dumps(get_config().get('layout', {}))
    return f"window._trotto = window._trotto || {{}}; window._trotto.layout = {layout_json};"
Пример #13
0
def layout_config():
    _config = (config.get_organization_config(current_user.organization)
               if current_user.is_authenticated else config.get_config())

    return f"window._trotto = window._trotto || {{}}; window._trotto.layout = {json.dumps(_config.get('layout', {}))};"
Пример #14
0
def init_app_without_routes(disable_csrf=False):
    app = Flask(__name__)

    app.secret_key = config.get_config()['sessions_secret']

    app.config['SQLALCHEMY_DATABASE_URI'] = config.get_config(
    )['postgres']['url']
    if app.config['SQLALCHEMY_DATABASE_URI'].startswith('postgres://'):
        # SQLAlchemy deprecated the `postgres` dialect, but it's still used by Heroku Postgres:
        # https://help.heroku.com/ZKNTJQSK/why-is-sqlalchemy-1-4-x-not-connecting-to-heroku-postgres
        app.config['SQLALCHEMY_DATABASE_URI'] = app.config[
            'SQLALCHEMY_DATABASE_URI'].replace('postgres://', 'postgresql://',
                                               1)

    if config.get_config()['postgres'].get('commercial_url'):
        app.config['SQLALCHEMY_BINDS'] = {
            'commercial': config.get_config()['postgres']['commercial_url']
        }

    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

    global db

    class SQLAlchemy(_BaseSQLAlchemy):
        def apply_pool_defaults(self, app, options):
            super(SQLAlchemy, self).apply_pool_defaults(app, options)
            options["pool_pre_ping"] = True

    db = SQLAlchemy(app)

    from modules.base import authentication

    if os.getenv('ENVIRONMENT') == 'test_env':
        app.before_request(authentication.login_test_user)

    @app.errorhandler(403)
    def handle_403(error):
        return jsonify({
            'error_type': 'error_bar',
            'error': error.description or ''
        }), 403

    login_manager = LoginManager()
    login_manager.init_app(app)

    global csrf_protect

    if not disable_csrf:
        app.before_request(authentication.check_csrf)

    @login_manager.user_loader
    def load_user(user_id):
        from modules.users.helpers import get_user_by_id

        sentry_sdk.set_user({'id': user_id})

        return get_user_by_id(user_id)

    app.before_request(authentication.validate_user_authentication)

    @app.route('/_/health_check')
    def health_check():
        return 'OK'

    return app
Пример #15
0

def _check_for_client_secrets():
    if not os.path.isfile(
            os.path.join(os.path.dirname(os.path.realpath(__file__)),
                         'src/config/client_secrets.json')):
        print(
            '\nTo deploy to App Engine, you must include a src/config/client_secrets.json. For guidance, see'
            ' https://github.com/trotto/go-links#obtain-oauth-client-credentials.'
        )

        sys.exit(1)


if __name__ == "__main__":
    secrets = get_config(False) or {}

    if 'sessions_secret' not in secrets:
        user_input = raw_input(
            "You don't yet have a sessions secret, so one will be created for you and stored"
            " in src/config/secrets.yaml. Be sure to store this secret somewhere safe."
            " Hit Enter to continue. ")
        print('\n')

        secrets['sessions_secret'] = generate_secret(64)

    if 'app_id' not in secrets:
        secrets['app_id'] = raw_input("What's your App Engine app ID? ")

    _write_secrets(secrets)