def save_token(token_data, request): requested_scopes = set(scope_to_list(token_data.get('scope', ''))) application = OAuthApplication.query.filter_by( client_id=request.client.client_id).one() link = OAuthApplicationUserLink.query.with_parent(application).with_parent( request.user).first() if link is None: link = OAuthApplicationUserLink(application=application, user=request.user, scopes=requested_scopes) else: if not requested_scopes: # for already-authorized apps not specifying a scope uses all scopes the # user previously granted to the app requested_scopes = set(link.scopes) token_data['scope'] = list_to_scope(requested_scopes) new_scopes = requested_scopes - set(link.scopes) if new_scopes: logger.info('New scopes for %r: %s', link, new_scopes) link.update_scopes(new_scopes) link.tokens.append( OAuthToken(access_token=token_data['access_token'], scopes=requested_scopes)) # get rid of old tokens if there are too many q = (db.session.query(OAuthToken.id).with_parent(link).filter_by( _scopes=db.cast(sorted(requested_scopes), ARRAY(db.String))).order_by( OAuthToken.created_dt.desc()).offset( MAX_TOKENS_PER_SCOPE).scalar_subquery()) OAuthToken.query.filter( OAuthToken.id.in_(q)).delete(synchronize_session='fetch')
def dummy_app_link(db, dummy_application, dummy_user): """Return an app link for the dummy user.""" link = OAuthApplicationUserLink(application=dummy_application, user=dummy_user, scopes=['read:legacy_api', 'read:user']) db.session.add(link) db.session.flush() return link
def test_merge_users(create_user, dummy_user, dummy_application, dummy_token, create_application, test_client): source_user = create_user(123) # app on both users (already exists on dummy user via dummy token) app_link = OAuthApplicationUserLink(application=dummy_application, user=source_user, scopes=['read:user', 'write:legacy_api']) token_string = generate_token() OAuthToken(access_token=token_string, app_user_link=app_link, scopes=['read:user']) # app only on source user test_app = create_application(name='test') app_link2 = OAuthApplicationUserLink(application=test_app, user=source_user, scopes=['read:user']) token_string2 = generate_token() OAuthToken(access_token=token_string2, app_user_link=app_link2, scopes=['read:user']) OAuthToken(access_token=generate_token(), app_user_link=app_link2, scopes=['read:user']) OAuthToken(access_token=generate_token(), app_user_link=app_link2, scopes=['read:user']) resp = test_client.get('/api/user/', headers={'Authorization': f'Bearer {dummy_token._plaintext_token}'}) assert resp.status_code == 200 assert resp.json['id'] == dummy_user.id for token in (token_string, token_string2): resp = test_client.get('/api/user/', headers={'Authorization': f'Bearer {token}'}) assert resp.status_code == 200 assert resp.json['id'] == source_user.id old_token_count = OAuthToken.query.count() merge_users(source_user, dummy_user) # source user should not have any leftover app links assert not source_user.oauth_app_links.count() # two app links on the target user assert dummy_user.oauth_app_links.count() == 2 # dummy app has one token from each user assert dummy_user.oauth_app_links.filter_by(application=dummy_application).one().tokens.count() == 2 # test app has 3 tokens coming from source user assert dummy_user.oauth_app_links.filter_by(application=test_app).one().tokens.count() == 3 # the total number of tokens didn't change (we do not delete surplus tokens during merge anyway) assert OAuthToken.query.count() == old_token_count # all tokens point to the target user for token in (dummy_token._plaintext_token, token_string, token_string2): resp = test_client.get('/api/user/', headers={'Authorization': f'Bearer {token}'}) assert resp.status_code == 200 assert resp.json['id'] == dummy_user.id
def test_get_request_user_oauth_querystring(db, dummy_user, app, test_client): # TODO: remove this once indico-checkin no longer sends tokens in the query string! from indico.core.oauth.models.applications import OAuthApplication, OAuthApplicationUserLink, SystemAppType from indico.core.oauth.models.tokens import OAuthToken @oauth_scope('registrants') class RHTest(RH): def _process(self): user, source = get_request_user() assert session.user == user if not user: return 'none' return f'{user.id}|{source}' app.add_url_rule('/test/registrants', 'test_registrants', make_view_func(RHTest), methods=('GET', 'POST')) checkin_app = OAuthApplication.query.filter_by( system_app_type=SystemAppType.checkin).one() app_link = OAuthApplicationUserLink(application=checkin_app, user=dummy_user, scopes=['registrants']) token_string = 'x' * 40 OAuthToken(access_token=token_string, app_user_link=app_link, scopes=['registrants']) db.session.flush() resp = test_client.get('/test/registrants', headers={'Authorization': f'Bearer {token_string}'}) assert resp.status_code == 200 assert resp.data == b'1337|oauth' resp = test_client.post( '/test/registrants', headers={'Authorization': f'Bearer {token_string}'}) assert resp.status_code == 200 assert resp.data == b'1337|oauth' resp = test_client.get(f'/test/registrants?access_token={token_string}') assert resp.status_code == 200 assert resp.data == b'1337|oauth' resp = test_client.post(f'/test/registrants?access_token={token_string}') assert resp.status_code == 200 assert resp.data == b'1337|oauth'
def test_checkin_app_querystring_tokens(db, test_client, dummy_user): checkin_app = OAuthApplication.query.filter_by( system_app_type=SystemAppType.checkin).one() checkin_app.allowed_scopes = ['read:user'] app_link = OAuthApplicationUserLink(application=checkin_app, user=dummy_user, scopes=['read:user']) token_string = generate_token() OAuthToken(access_token=token_string, app_user_link=app_link, scopes=['read:user']) db.session.flush() resp = test_client.get('/api/user/', headers={'Authorization': f'Bearer {token_string}'}) assert resp.status_code == 200 resp = test_client.get(f'/api/user/?access_token={token_string}') assert resp.status_code == 200