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
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
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
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('/')
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
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'))
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
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())
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())
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')
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
def layout_config(): layout_json = json.dumps(get_config().get('layout', {})) return f"window._trotto = window._trotto || {{}}; window._trotto.layout = {layout_json};"
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', {}))};"
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
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)