users: Get all API keys via wrapper method.

Now reading API keys from a user is done with the get_api_key wrapper
method, rather than directly fetching it from the user object.

Also, every place where an action should be done for each API key is now
using get_all_api_keys. This method returns for the moment a single-item
list, containing the specified user's API key.

This commit is the first step towards allowing users have multiple API
keys.
This commit is contained in:
Yago González 2018-08-01 10:53:40 +02:00 committed by Tim Abbott
parent 13b9dd33fa
commit f6219745de
20 changed files with 110 additions and 52 deletions

View File

@ -23,6 +23,7 @@ from zerver.lib.api_test_helpers import test_the_api, test_invalid_api_key, \
os.environ['DJANGO_SETTINGS_MODULE'] = 'zproject.test_settings' os.environ['DJANGO_SETTINGS_MODULE'] = 'zproject.test_settings'
django.setup() django.setup()
from zerver.lib.actions import do_create_user from zerver.lib.actions import do_create_user
from zerver.lib.users import get_api_key
from zerver.models import get_user, get_realm from zerver.models import get_user, get_realm
usage = """test-js-with-casper [options]""" usage = """test-js-with-casper [options]"""
@ -45,7 +46,8 @@ with test_server_running(force=options.force, external_host='zulipdev.com:9981')
# Prepare the admin client # Prepare the admin client
email = 'iago@zulip.com' # Iago is an admin email = 'iago@zulip.com' # Iago is an admin
realm = get_realm("zulip") realm = get_realm("zulip")
api_key = get_user(email, realm).api_key user = get_user(email, realm)
api_key = get_api_key(user)
site = 'http://zulip.zulipdev.com:9981' site = 'http://zulip.zulipdev.com:9981'
client = Client( client = Client(
@ -58,7 +60,7 @@ with test_server_running(force=options.force, external_host='zulipdev.com:9981')
email = 'guest@zulip.com' # guest is not an admin email = 'guest@zulip.com' # guest is not an admin
guest_user = do_create_user('guest@zulip.com', 'secret', guest_user = do_create_user('guest@zulip.com', 'secret',
get_realm('zulip'), 'Mr. Guest', 'guest') get_realm('zulip'), 'Mr. Guest', 'guest')
api_key = guest_user.api_key api_key = get_api_key(guest_user)
nonadmin_client = Client( nonadmin_client = Client(
email=email, email=email,
api_key=api_key, api_key=api_key,

View File

@ -60,7 +60,12 @@ from zerver.lib.topic_mutes import (
add_topic_mute, add_topic_mute,
remove_topic_mute, remove_topic_mute,
) )
from zerver.lib.users import bulk_get_users, check_full_name, user_ids_to_users from zerver.lib.users import (
bulk_get_users,
check_full_name,
get_api_key,
user_ids_to_users
)
from zerver.lib.user_groups import create_user_group, access_user_group_by_id from zerver.lib.user_groups import create_user_group, access_user_group_by_id
from zerver.models import Realm, RealmEmoji, Stream, UserProfile, UserActivity, \ from zerver.models import Realm, RealmEmoji, Stream, UserProfile, UserActivity, \
RealmDomain, Service, SubMessage, \ RealmDomain, Service, SubMessage, \
@ -498,7 +503,7 @@ def created_bot_event(user_profile: UserProfile) -> Dict[str, Any]:
full_name=user_profile.full_name, full_name=user_profile.full_name,
bot_type=user_profile.bot_type, bot_type=user_profile.bot_type,
is_active=user_profile.is_active, is_active=user_profile.is_active,
api_key=user_profile.api_key, api_key=get_api_key(user_profile),
default_sending_stream=default_sending_stream_name, default_sending_stream=default_sending_stream_name,
default_events_register_stream=default_events_register_stream_name, default_events_register_stream=default_events_register_stream_name,
default_all_public_streams=user_profile.default_all_public_streams, default_all_public_streams=user_profile.default_all_public_streams,

View File

@ -349,11 +349,14 @@ def get_stream_cache_key(stream_name: str, realm_id: int) -> str:
realm_id, make_safe_digest(stream_name.strip().lower())) realm_id, make_safe_digest(stream_name.strip().lower()))
def delete_user_profile_caches(user_profiles: Iterable['UserProfile']) -> None: def delete_user_profile_caches(user_profiles: Iterable['UserProfile']) -> None:
# Imported here to avoid cyclic dependency.
from zerver.lib.users import get_all_api_keys
keys = [] keys = []
for user_profile in user_profiles: for user_profile in user_profiles:
keys.append(user_profile_by_email_cache_key(user_profile.email)) keys.append(user_profile_by_email_cache_key(user_profile.email))
keys.append(user_profile_by_id_cache_key(user_profile.id)) keys.append(user_profile_by_id_cache_key(user_profile.id))
keys.append(user_profile_by_api_key_cache_key(user_profile.api_key)) for api_key in get_all_api_keys(user_profile):
keys.append(user_profile_by_api_key_cache_key(api_key))
keys.append(user_profile_cache_key(user_profile.email, user_profile.realm)) keys.append(user_profile_cache_key(user_profile.email, user_profile.realm))
cache_delete_many(keys) cache_delete_many(keys)

View File

@ -18,6 +18,7 @@ from zerver.lib.cache import cache_with_key, cache_set, \
user_profile_cache_key, get_remote_cache_time, get_remote_cache_requests, \ user_profile_cache_key, get_remote_cache_time, get_remote_cache_requests, \
cache_set_many, to_dict_cache_key_id cache_set_many, to_dict_cache_key_id
from zerver.lib.message import MessageDict from zerver.lib.message import MessageDict
from zerver.lib.users import get_all_api_keys
from importlib import import_module from importlib import import_module
from django.contrib.sessions.models import Session from django.contrib.sessions.models import Session
from django.db.models import Q from django.db.models import Q
@ -45,7 +46,8 @@ def message_cache_items(items_for_remote_cache: Dict[str, Tuple[bytes]],
def user_cache_items(items_for_remote_cache: Dict[str, Tuple[UserProfile]], def user_cache_items(items_for_remote_cache: Dict[str, Tuple[UserProfile]],
user_profile: UserProfile) -> None: user_profile: UserProfile) -> None:
items_for_remote_cache[user_profile_by_api_key_cache_key(user_profile.api_key)] = (user_profile,) for api_key in get_all_api_keys(user_profile):
items_for_remote_cache[user_profile_by_api_key_cache_key(api_key)] = (user_profile,)
items_for_remote_cache[user_profile_cache_key(user_profile.email, user_profile.realm)] = (user_profile,) items_for_remote_cache[user_profile_cache_key(user_profile.email, user_profile.realm)] = (user_profile,)
# We have other user_profile caches, but none of them are on the # We have other user_profile caches, but none of them are on the
# core serving path for lots of requests. # core serving path for lots of requests.

View File

@ -20,6 +20,7 @@ from django.http import HttpRequest
from two_factor.models import PhoneDevice from two_factor.models import PhoneDevice
from zerver.lib.initial_password import initial_password from zerver.lib.initial_password import initial_password
from zerver.lib.utils import is_remote_server from zerver.lib.utils import is_remote_server
from zerver.lib.users import get_api_key
from zerver.lib.actions import ( from zerver.lib.actions import (
check_send_message, create_stream_if_needed, bulk_add_subscriptions, check_send_message, create_stream_if_needed, bulk_add_subscriptions,
@ -370,7 +371,8 @@ class ZulipTestCase(TestCase):
if is_remote_server(identifier): if is_remote_server(identifier):
api_key = get_remote_server_by_uuid(identifier).api_key api_key = get_remote_server_by_uuid(identifier).api_key
else: else:
api_key = get_user(identifier, get_realm(realm)).api_key user = get_user(identifier, get_realm(realm))
api_key = get_api_key(user)
API_KEYS[identifier] = api_key API_KEYS[identifier] = api_key
credentials = "%s:%s" % (identifier, api_key) credentials = "%s:%s" % (identifier, api_key)
@ -707,7 +709,7 @@ class WebhookTestCase(ZulipTestCase):
def build_webhook_url(self, *args: Any, **kwargs: Any) -> str: def build_webhook_url(self, *args: Any, **kwargs: Any) -> str:
url = self.URL_TEMPLATE url = self.URL_TEMPLATE
if url.find("api_key") >= 0: if url.find("api_key") >= 0:
api_key = self.test_user.api_key api_key = get_api_key(self.test_user)
url = self.URL_TEMPLATE.format(api_key=api_key, url = self.URL_TEMPLATE.format(api_key=api_key,
stream=self.STREAM_NAME) stream=self.STREAM_NAME)
else: else:

View File

@ -179,3 +179,10 @@ def get_accounts_for_email(email: str) -> List[Dict[str, Optional[str]]]:
"full_name": profile.full_name, "full_name": profile.full_name,
"avatar": avatar_url(profile)} "avatar": avatar_url(profile)}
for profile in profiles] for profile in profiles]
def get_api_key(user_profile: UserProfile) -> str:
return user_profile.api_key
def get_all_api_keys(user_profile: UserProfile) -> List[str]:
# Users can only have one API key for now
return [user_profile.api_key]

View File

@ -33,6 +33,7 @@ from zerver.lib.mobile_auth_otp import otp_decrypt_api_key
from zerver.lib.validator import validate_login_email, \ from zerver.lib.validator import validate_login_email, \
check_bool, check_dict_only, check_string, Validator check_bool, check_dict_only, check_string, Validator
from zerver.lib.request import JsonableError from zerver.lib.request import JsonableError
from zerver.lib.users import get_all_api_keys
from zerver.lib.initial_password import initial_password from zerver.lib.initial_password import initial_password
from zerver.lib.sessions import get_session_dict_user from zerver.lib.sessions import get_session_dict_user
from zerver.lib.test_classes import ( from zerver.lib.test_classes import (
@ -676,8 +677,8 @@ class GitHubAuthBackendTest(ZulipTestCase):
self.assertEqual(query_params["realm"], ['http://zulip.testserver']) self.assertEqual(query_params["realm"], ['http://zulip.testserver'])
self.assertEqual(query_params["email"], [self.example_email("hamlet")]) self.assertEqual(query_params["email"], [self.example_email("hamlet")])
encrypted_api_key = query_params["otp_encrypted_api_key"][0] encrypted_api_key = query_params["otp_encrypted_api_key"][0]
self.assertEqual(self.example_user('hamlet').api_key, hamlet_api_keys = get_all_api_keys(self.example_user('hamlet'))
otp_decrypt_api_key(encrypted_api_key, mobile_flow_otp)) self.assertIn(otp_decrypt_api_key(encrypted_api_key, mobile_flow_otp), hamlet_api_keys)
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
self.assertIn('Zulip on Android', mail.outbox[0].body) self.assertIn('Zulip on Android', mail.outbox[0].body)
@ -931,8 +932,8 @@ class GoogleSubdomainLoginTest(GoogleOAuthTest):
self.assertEqual(query_params["realm"], ['http://zulip.testserver']) self.assertEqual(query_params["realm"], ['http://zulip.testserver'])
self.assertEqual(query_params["email"], [self.example_email("hamlet")]) self.assertEqual(query_params["email"], [self.example_email("hamlet")])
encrypted_api_key = query_params["otp_encrypted_api_key"][0] encrypted_api_key = query_params["otp_encrypted_api_key"][0]
self.assertEqual(self.example_user('hamlet').api_key, hamlet_api_keys = get_all_api_keys(self.example_user('hamlet'))
otp_decrypt_api_key(encrypted_api_key, mobile_flow_otp)) self.assertIn(otp_decrypt_api_key(encrypted_api_key, mobile_flow_otp), hamlet_api_keys)
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
self.assertIn('Zulip on Android', mail.outbox[0].body) self.assertIn('Zulip on Android', mail.outbox[0].body)
@ -1446,7 +1447,8 @@ class DevFetchAPIKeyTest(ZulipTestCase):
self.assert_json_success(result) self.assert_json_success(result)
data = result.json() data = result.json()
self.assertEqual(data["email"], self.email) self.assertEqual(data["email"], self.email)
self.assertEqual(data['api_key'], self.user_profile.api_key) user_api_keys = get_all_api_keys(self.user_profile)
self.assertIn(data['api_key'], user_api_keys)
def test_invalid_email(self) -> None: def test_invalid_email(self) -> None:
email = 'hamlet' email = 'hamlet'
@ -1924,8 +1926,8 @@ class TestZulipRemoteUserBackend(ZulipTestCase):
self.assertEqual(query_params["realm"], ['http://zulip.testserver']) self.assertEqual(query_params["realm"], ['http://zulip.testserver'])
self.assertEqual(query_params["email"], [self.example_email("hamlet")]) self.assertEqual(query_params["email"], [self.example_email("hamlet")])
encrypted_api_key = query_params["otp_encrypted_api_key"][0] encrypted_api_key = query_params["otp_encrypted_api_key"][0]
self.assertEqual(self.example_user('hamlet').api_key, hamlet_api_keys = get_all_api_keys(self.example_user('hamlet'))
otp_decrypt_api_key(encrypted_api_key, mobile_flow_otp)) self.assertIn(otp_decrypt_api_key(encrypted_api_key, mobile_flow_otp), hamlet_api_keys)
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
self.assertIn('Zulip on Android', mail.outbox[0].body) self.assertIn('Zulip on Android', mail.outbox[0].body)

View File

@ -26,6 +26,7 @@ from zerver.lib.test_classes import (
WebhookTestCase, WebhookTestCase,
) )
from zerver.lib.response import json_response from zerver.lib.response import json_response
from zerver.lib.users import get_api_key
from zerver.lib.user_agent import parse_user_agent from zerver.lib.user_agent import parse_user_agent
from zerver.lib.request import \ from zerver.lib.request import \
REQ, has_request_variables, RequestVariableMissingError, \ REQ, has_request_variables, RequestVariableMissingError, \
@ -249,7 +250,7 @@ class DecoratorTestCase(TestCase):
webhook_bot_email = 'webhook-bot@zulip.com' webhook_bot_email = 'webhook-bot@zulip.com'
webhook_bot_realm = get_realm('zulip') webhook_bot_realm = get_realm('zulip')
webhook_bot = get_user(webhook_bot_email, webhook_bot_realm) webhook_bot = get_user(webhook_bot_email, webhook_bot_realm)
webhook_bot_api_key = webhook_bot.api_key webhook_bot_api_key = get_api_key(webhook_bot)
webhook_client_name = "ZulipClientNameWebhook" webhook_client_name = "ZulipClientNameWebhook"
request = HostRequestMock() request = HostRequestMock()
@ -369,7 +370,7 @@ class DecoratorLoggingTestCase(ZulipTestCase):
webhook_bot_email = 'webhook-bot@zulip.com' webhook_bot_email = 'webhook-bot@zulip.com'
webhook_bot_realm = get_realm('zulip') webhook_bot_realm = get_realm('zulip')
webhook_bot = get_user(webhook_bot_email, webhook_bot_realm) webhook_bot = get_user(webhook_bot_email, webhook_bot_realm)
webhook_bot_api_key = webhook_bot.api_key webhook_bot_api_key = get_api_key(webhook_bot)
request = HostRequestMock() request = HostRequestMock()
request.method = 'POST' request.method = 'POST'
@ -470,7 +471,8 @@ body:
def test_authenticated_rest_api_view_errors(self) -> None: def test_authenticated_rest_api_view_errors(self) -> None:
user_profile = self.example_user("hamlet") user_profile = self.example_user("hamlet")
credentials = "%s:%s" % (user_profile.email, user_profile.api_key) api_key = get_api_key(user_profile)
credentials = "%s:%s" % (user_profile.email, api_key)
api_auth = 'Digest ' + base64.b64encode(credentials.encode('utf-8')).decode('utf-8') api_auth = 'Digest ' + base64.b64encode(credentials.encode('utf-8')).decode('utf-8')
result = self.client_post('/api/v1/external/zendesk', {}, result = self.client_post('/api/v1/external/zendesk', {},
HTTP_AUTHORIZATION=api_auth) HTTP_AUTHORIZATION=api_auth)
@ -856,8 +858,8 @@ class DeactivatedRealmTest(ZulipTestCase):
""" """
do_deactivate_realm(get_realm("zulip")) do_deactivate_realm(get_realm("zulip"))
user_profile = self.example_user("hamlet") user_profile = self.example_user("hamlet")
url = "/api/v1/external/jira?api_key=%s&stream=jira_custom" % ( api_key = get_api_key(user_profile)
user_profile.api_key,) url = "/api/v1/external/jira?api_key=%s&stream=jira_custom" % (api_key,)
data = self.webhook_fixture_data('jira', 'created_v2') data = self.webhook_fixture_data('jira', 'created_v2')
result = self.client_post(url, data, result = self.client_post(url, data,
content_type="application/json") content_type="application/json")
@ -1029,8 +1031,8 @@ class InactiveUserTest(ZulipTestCase):
user_profile = self.example_user('hamlet') user_profile = self.example_user('hamlet')
do_deactivate_user(user_profile) do_deactivate_user(user_profile)
url = "/api/v1/external/jira?api_key=%s&stream=jira_custom" % ( api_key = get_api_key(user_profile)
user_profile.api_key,) url = "/api/v1/external/jira?api_key=%s&stream=jira_custom" % (api_key,)
data = self.webhook_fixture_data('jira', 'created_v2') data = self.webhook_fixture_data('jira', 'created_v2')
result = self.client_post(url, data, result = self.client_post(url, data,
content_type="application/json") content_type="application/json")
@ -1072,36 +1074,41 @@ class TestValidateApiKey(ZulipTestCase):
# We use default_bot's key but webhook_bot's email address to test # We use default_bot's key but webhook_bot's email address to test
# the logic when an API key is passed and it doesn't belong to the # the logic when an API key is passed and it doesn't belong to the
# user whose email address has been provided. # user whose email address has been provided.
validate_api_key(HostRequestMock(), self.webhook_bot.email, self.default_bot.api_key) api_key = get_api_key(self.default_bot)
validate_api_key(HostRequestMock(), self.webhook_bot.email, api_key)
def test_validate_api_key_if_profile_is_not_active(self) -> None: def test_validate_api_key_if_profile_is_not_active(self) -> None:
self._change_is_active_field(self.default_bot, False) self._change_is_active_field(self.default_bot, False)
with self.assertRaises(JsonableError): with self.assertRaises(JsonableError):
validate_api_key(HostRequestMock(), self.default_bot.email, self.default_bot.api_key) api_key = get_api_key(self.default_bot)
validate_api_key(HostRequestMock(), self.default_bot.email, api_key)
self._change_is_active_field(self.default_bot, True) self._change_is_active_field(self.default_bot, True)
def test_validate_api_key_if_profile_is_incoming_webhook_and_is_webhook_is_unset(self) -> None: def test_validate_api_key_if_profile_is_incoming_webhook_and_is_webhook_is_unset(self) -> None:
with self.assertRaises(JsonableError): with self.assertRaises(JsonableError):
validate_api_key(HostRequestMock(), self.webhook_bot.email, self.webhook_bot.api_key) api_key = get_api_key(self.webhook_bot)
validate_api_key(HostRequestMock(), self.webhook_bot.email, api_key)
def test_validate_api_key_if_profile_is_incoming_webhook_and_is_webhook_is_set(self) -> None: def test_validate_api_key_if_profile_is_incoming_webhook_and_is_webhook_is_set(self) -> None:
api_key = get_api_key(self.webhook_bot)
profile = validate_api_key(HostRequestMock(host="zulip.testserver"), profile = validate_api_key(HostRequestMock(host="zulip.testserver"),
self.webhook_bot.email, self.webhook_bot.api_key, self.webhook_bot.email, api_key,
is_webhook=True) is_webhook=True)
self.assertEqual(profile.id, self.webhook_bot.id) self.assertEqual(profile.id, self.webhook_bot.id)
def test_validate_api_key_if_email_is_case_insensitive(self) -> None: def test_validate_api_key_if_email_is_case_insensitive(self) -> None:
profile = validate_api_key(HostRequestMock(host="zulip.testserver"), self.default_bot.email.upper(), self.default_bot.api_key) api_key = get_api_key(self.default_bot)
profile = validate_api_key(HostRequestMock(host="zulip.testserver"), self.default_bot.email.upper(), api_key)
self.assertEqual(profile.id, self.default_bot.id) self.assertEqual(profile.id, self.default_bot.id)
def test_valid_api_key_if_user_is_on_wrong_subdomain(self) -> None: def test_valid_api_key_if_user_is_on_wrong_subdomain(self) -> None:
with self.settings(RUNNING_INSIDE_TORNADO=False): with self.settings(RUNNING_INSIDE_TORNADO=False):
api_key = get_api_key(self.default_bot)
with mock.patch('logging.warning') as mock_warning: with mock.patch('logging.warning') as mock_warning:
with self.assertRaisesRegex(JsonableError, with self.assertRaisesRegex(JsonableError,
"Account is not associated with this subdomain"): "Account is not associated with this subdomain"):
validate_api_key(HostRequestMock(host=settings.EXTERNAL_HOST), validate_api_key(HostRequestMock(host=settings.EXTERNAL_HOST),
self.default_bot.email, self.default_bot.email, api_key)
self.default_bot.api_key)
mock_warning.assert_called_with( mock_warning.assert_called_with(
"User {} ({}) attempted to access API on wrong " "User {} ({}) attempted to access API on wrong "
@ -1111,8 +1118,7 @@ class TestValidateApiKey(ZulipTestCase):
with self.assertRaisesRegex(JsonableError, with self.assertRaisesRegex(JsonableError,
"Account is not associated with this subdomain"): "Account is not associated with this subdomain"):
validate_api_key(HostRequestMock(host='acme.' + settings.EXTERNAL_HOST), validate_api_key(HostRequestMock(host='acme.' + settings.EXTERNAL_HOST),
self.default_bot.email, self.default_bot.email, api_key)
self.default_bot.api_key)
mock_warning.assert_called_with( mock_warning.assert_called_with(
"User {} ({}) attempted to access API on wrong " "User {} ({}) attempted to access API on wrong "

View File

@ -119,6 +119,7 @@ from zerver.lib.validator import (
equals, check_none_or, Validator, check_url equals, check_none_or, Validator, check_url
) )
from zerver.lib.upload import upload_backend, attachment_url_to_path_id from zerver.lib.upload import upload_backend, attachment_url_to_path_id
from zerver.lib.users import get_api_key
from zerver.views.events_register import _default_all_public_streams, _default_narrow from zerver.views.events_register import _default_all_public_streams, _default_narrow
@ -2341,7 +2342,7 @@ class FetchInitialStateDataTest(ZulipTestCase):
self.assert_length(result['realm_bots'], 0) self.assert_length(result['realm_bots'], 0)
# additionally the API key for a random bot is not present in the data # additionally the API key for a random bot is not present in the data
api_key = self.notification_bot().api_key api_key = get_api_key(self.notification_bot())
self.assertNotIn(api_key, str(result)) self.assertNotIn(api_key, str(result))
# Admin users have access to all bots in the realm_bots field # Admin users have access to all bots in the realm_bots field

View File

@ -10,6 +10,7 @@ from zerver.lib.initial_password import initial_password
from zerver.lib.sessions import get_session_dict_user from zerver.lib.sessions import get_session_dict_user
from zerver.lib.test_classes import ZulipTestCase from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.test_helpers import MockLDAP from zerver.lib.test_helpers import MockLDAP
from zerver.lib.users import get_all_api_keys
from zerver.models import get_realm, get_user, UserProfile from zerver.models import get_realm, get_user, UserProfile
class ChangeSettingsTest(ZulipTestCase): class ChangeSettingsTest(ZulipTestCase):
@ -286,10 +287,11 @@ class UserChangesTest(ZulipTestCase):
user = self.example_user('hamlet') user = self.example_user('hamlet')
email = user.email email = user.email
self.login(email) self.login(email)
old_api_key = user.api_key old_api_keys = get_all_api_keys(user)
result = self.client_post('/json/users/me/api_key/regenerate') result = self.client_post('/json/users/me/api_key/regenerate')
self.assert_json_success(result) self.assert_json_success(result)
new_api_key = result.json()['api_key'] new_api_key = result.json()['api_key']
self.assertNotEqual(old_api_key, new_api_key) self.assertNotIn(new_api_key, old_api_keys)
user = self.example_user('hamlet') user = self.example_user('hamlet')
self.assertEqual(new_api_key, user.api_key) current_api_keys = get_all_api_keys(user)
self.assertIn(new_api_key, current_api_keys)

View File

@ -3,6 +3,7 @@ from django.conf import settings
from zerver.lib.test_classes import ZulipTestCase, UploadSerializeMixin from zerver.lib.test_classes import ZulipTestCase, UploadSerializeMixin
from zerver.lib.test_helpers import use_s3_backend, override_settings from zerver.lib.test_helpers import use_s3_backend, override_settings
from zerver.lib.users import get_api_key
from io import StringIO from io import StringIO
from boto.s3.connection import S3Connection from boto.s3.connection import S3Connection
@ -97,14 +98,15 @@ class ThumbnailTest(ZulipTestCase):
# Test api endpoint with legacy API authentication. # Test api endpoint with legacy API authentication.
user_profile = self.example_user("hamlet") user_profile = self.example_user("hamlet")
result = self.client_get("/thumbnail?url=%s&size=thumbnail&api_key=%s" % ( result = self.client_get("/thumbnail?url=%s&size=thumbnail&api_key=%s" % (
quoted_url, user_profile.api_key)) quoted_url, get_api_key(user_profile)))
self.assertEqual(result.status_code, 302, result) self.assertEqual(result.status_code, 302, result)
expected_part_url = '/0x100/smart/filters:no_upscale()/' + encoded_url + '/source_type/external' expected_part_url = '/0x100/smart/filters:no_upscale()/' + encoded_url + '/source_type/external'
self.assertIn(expected_part_url, result.url) self.assertIn(expected_part_url, result.url)
# Test a second logged-in user; they should also be able to access it # Test a second logged-in user; they should also be able to access it
user_profile = self.example_user("iago") user_profile = self.example_user("iago")
result = self.client_get("/thumbnail?url=%s&size=thumbnail&api_key=%s" % (quoted_url, user_profile.api_key)) result = self.client_get("/thumbnail?url=%s&size=thumbnail&api_key=%s" % (
quoted_url, get_api_key(user_profile)))
self.assertEqual(result.status_code, 302, result) self.assertEqual(result.status_code, 302, result)
expected_part_url = '/0x100/smart/filters:no_upscale()/' + encoded_url + '/source_type/external' expected_part_url = '/0x100/smart/filters:no_upscale()/' + encoded_url + '/source_type/external'
self.assertIn(expected_part_url, result.url) self.assertIn(expected_part_url, result.url)
@ -194,7 +196,7 @@ class ThumbnailTest(ZulipTestCase):
user_profile = self.example_user("hamlet") user_profile = self.example_user("hamlet")
result = self.client_get( result = self.client_get(
'/thumbnail?url=%s&size=full&api_key=%s' % '/thumbnail?url=%s&size=full&api_key=%s' %
(quoted_uri, user_profile.api_key)) (quoted_uri, get_api_key(user_profile)))
self.assertEqual(result.status_code, 302, result) self.assertEqual(result.status_code, 302, result)
expected_part_url = get_file_path_urlpart(uri) expected_part_url = get_file_path_urlpart(uri)
self.assertIn(expected_part_url, result.url) self.assertIn(expected_part_url, result.url)

View File

@ -36,6 +36,7 @@ from zerver.lib.actions import (
) )
from zerver.lib.create_user import copy_user_settings from zerver.lib.create_user import copy_user_settings
from zerver.lib.request import JsonableError from zerver.lib.request import JsonableError
from zerver.lib.users import get_api_key
from zerver.views.upload import upload_file_backend, serve_local from zerver.views.upload import upload_file_backend, serve_local
import urllib import urllib
@ -115,7 +116,7 @@ class FileUploadTest(UploadSerializeMixin, ZulipTestCase):
response = self.client_get(uri + "?api_key=" + "invalid") response = self.client_get(uri + "?api_key=" + "invalid")
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
response = self.client_get(uri + "?api_key=" + user_profile.api_key) response = self.client_get(uri + "?api_key=" + get_api_key(user_profile))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
data = b"".join(response.streaming_content) data = b"".join(response.streaming_content)
self.assertEqual(b"zulip!", data) self.assertEqual(b"zulip!", data)

View File

@ -6,6 +6,7 @@ from mock import patch
from typing import Any, Dict from typing import Any, Dict
from zerver.lib.test_classes import ZulipTestCase from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.users import get_api_key
from zerver.models import get_user, get_realm from zerver.models import get_user, get_realm
@ -27,6 +28,8 @@ class ZephyrTest(ZulipTestCase):
email = str(self.mit_email("starnine")) email = str(self.mit_email("starnine"))
realm = get_realm('zephyr') realm = get_realm('zephyr')
user = get_user(email, realm)
api_key = get_api_key(user)
self.login(email, realm=realm) self.login(email, realm=realm)
def ccache_mock(**kwargs: Any) -> Any: def ccache_mock(**kwargs: Any) -> Any:
@ -66,7 +69,7 @@ class ZephyrTest(ZulipTestCase):
'--', '--',
'/home/zulip/python-zulip-api/zulip/integrations/zephyr/process_ccache', '/home/zulip/python-zulip-api/zulip/integrations/zephyr/process_ccache',
'starnine', 'starnine',
get_user(email, realm).api_key, api_key,
'MTIzNA==']) 'MTIzNA=='])
# Accounts whose Kerberos usernames are known not to match their # Accounts whose Kerberos usernames are known not to match their
@ -92,5 +95,5 @@ class ZephyrTest(ZulipTestCase):
'--', '--',
'/home/zulip/python-zulip-api/zulip/integrations/zephyr/process_ccache', '/home/zulip/python-zulip-api/zulip/integrations/zephyr/process_ccache',
'starnine', 'starnine',
get_user(email, realm).api_key, api_key,
'MTIzNA==']) 'MTIzNA=='])

View File

@ -32,6 +32,7 @@ from zerver.lib.push_notifications import push_notifications_enabled
from zerver.lib.request import REQ, has_request_variables, JsonableError from zerver.lib.request import REQ, has_request_variables, JsonableError
from zerver.lib.response import json_success, json_error from zerver.lib.response import json_success, json_error
from zerver.lib.subdomains import get_subdomain, is_subdomain_root_or_alias from zerver.lib.subdomains import get_subdomain, is_subdomain_root_or_alias
from zerver.lib.users import get_api_key
from zerver.lib.validator import validate_login_email from zerver.lib.validator import validate_login_email
from zerver.models import PreregistrationUser, UserProfile, remote_user_to_email, Realm, \ from zerver.models import PreregistrationUser, UserProfile, remote_user_to_email, Realm, \
get_realm get_realm
@ -730,7 +731,8 @@ def api_dev_fetch_api_key(request: HttpRequest, username: str=REQ()) -> HttpResp
return json_error(_("This user is not registered."), return json_error(_("This user is not registered."),
data={"reason": "unregistered"}, status=403) data={"reason": "unregistered"}, status=403)
do_login(request, user_profile) do_login(request, user_profile)
return json_success({"api_key": user_profile.api_key, "email": user_profile.email}) api_key = get_api_key(user_profile)
return json_success({"api_key": api_key, "email": user_profile.email})
@csrf_exempt @csrf_exempt
def api_dev_list_users(request: HttpRequest) -> HttpResponse: def api_dev_list_users(request: HttpRequest) -> HttpResponse:
@ -793,7 +795,8 @@ def api_fetch_api_key(request: HttpRequest, username: str=REQ(), password: str=R
process_client(request, user_profile) process_client(request, user_profile)
request._email = user_profile.email request._email = user_profile.email
return json_success({"api_key": user_profile.api_key, "email": user_profile.email}) api_key = get_api_key(user_profile)
return json_success({"api_key": api_key, "email": user_profile.email})
def get_auth_backends_data(request: HttpRequest) -> Dict[str, Any]: def get_auth_backends_data(request: HttpRequest) -> Dict[str, Any]:
"""Returns which authentication methods are enabled on the server""" """Returns which authentication methods are enabled on the server"""
@ -863,7 +866,9 @@ def json_fetch_api_key(request: HttpRequest, user_profile: UserProfile,
if not authenticate(username=user_profile.email, password=password, if not authenticate(username=user_profile.email, password=password,
realm=realm): realm=realm):
return json_error(_("Your username or password is incorrect.")) return json_error(_("Your username or password is incorrect."))
return json_success({"api_key": user_profile.api_key})
api_key = get_api_key(user_profile)
return json_success({"api_key": api_key})
@csrf_exempt @csrf_exempt
def api_fetch_google_client_id(request: HttpRequest) -> HttpResponse: def api_fetch_google_client_id(request: HttpRequest) -> HttpResponse:

View File

@ -27,6 +27,7 @@ from zerver.lib.request import has_request_variables, REQ
from zerver.lib.response import json_error, json_success from zerver.lib.response import json_error, json_success
from zerver.lib.streams import access_stream_by_name from zerver.lib.streams import access_stream_by_name
from zerver.lib.upload import upload_avatar_image from zerver.lib.upload import upload_avatar_image
from zerver.lib.users import get_api_key
from zerver.lib.validator import check_bool, check_string, check_int, check_url, check_dict from zerver.lib.validator import check_bool, check_string, check_int, check_url, check_dict
from zerver.lib.users import check_valid_bot_type, check_bot_creation_policy, \ from zerver.lib.users import check_valid_bot_type, check_bot_creation_policy, \
check_full_name, check_short_name, check_valid_interface_type, check_valid_bot_config, \ check_full_name, check_short_name, check_valid_interface_type, check_valid_bot_config, \
@ -317,8 +318,10 @@ def add_bot_backend(
notify_created_bot(bot_profile) notify_created_bot(bot_profile)
api_key = get_api_key(bot_profile)
json_result = dict( json_result = dict(
api_key=bot_profile.api_key, api_key=api_key,
avatar_url=avatar_url(bot_profile), avatar_url=avatar_url(bot_profile),
default_sending_stream=get_stream_name(bot_profile.default_sending_stream), default_sending_stream=get_stream_name(bot_profile.default_sending_stream),
default_events_register_stream=get_stream_name(bot_profile.default_events_register_stream), default_events_register_stream=get_stream_name(bot_profile.default_events_register_stream),
@ -337,10 +340,15 @@ def get_bots_backend(request: HttpRequest, user_profile: UserProfile) -> HttpRes
default_sending_stream = get_stream_name(bot_profile.default_sending_stream) default_sending_stream = get_stream_name(bot_profile.default_sending_stream)
default_events_register_stream = get_stream_name(bot_profile.default_events_register_stream) default_events_register_stream = get_stream_name(bot_profile.default_events_register_stream)
# Bots are supposed to have only one API key, at least for now.
# Therefore we can safely asume that one and only valid API key will be
# the first one.
api_key = get_api_key(bot_profile)
return dict( return dict(
username=bot_profile.email, username=bot_profile.email,
full_name=bot_profile.full_name, full_name=bot_profile.full_name,
api_key=bot_profile.api_key, api_key=api_key,
avatar_url=avatar_url(bot_profile), avatar_url=avatar_url(bot_profile),
default_sending_stream=default_sending_stream, default_sending_stream=default_sending_stream,
default_events_register_stream=default_events_register_stream, default_events_register_stream=default_events_register_stream,

View File

@ -8,6 +8,7 @@ from zerver.lib.ccache import make_ccache
from zerver.lib.request import has_request_variables, REQ, JsonableError from zerver.lib.request import has_request_variables, REQ, JsonableError
from zerver.lib.response import json_success, json_error from zerver.lib.response import json_success, json_error
from zerver.lib.str_utils import force_str from zerver.lib.str_utils import force_str
from zerver.lib.users import get_api_key
from zerver.models import UserProfile from zerver.models import UserProfile
import base64 import base64
@ -44,10 +45,11 @@ def webathena_kerberos_login(request: HttpRequest, user_profile: UserProfile,
# TODO: Send these data via (say) rabbitmq # TODO: Send these data via (say) rabbitmq
try: try:
api_key = get_api_key(user_profile)
subprocess.check_call(["ssh", settings.PERSONAL_ZMIRROR_SERVER, "--", subprocess.check_call(["ssh", settings.PERSONAL_ZMIRROR_SERVER, "--",
"/home/zulip/python-zulip-api/zulip/integrations/zephyr/process_ccache", "/home/zulip/python-zulip-api/zulip/integrations/zephyr/process_ccache",
force_str(user), force_str(user),
force_str(user_profile.api_key), force_str(api_key),
force_str(base64.b64encode(ccache))]) force_str(base64.b64encode(ccache))])
except Exception: except Exception:
logging.exception("Error updating the user's ccache") logging.exception("Error updating the user's ccache")

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from zerver.lib.test_classes import WebhookTestCase from zerver.lib.test_classes import WebhookTestCase
from zerver.lib.users import get_api_key
class DropboxHookTests(WebhookTestCase): class DropboxHookTests(WebhookTestCase):
STREAM_NAME = 'test' STREAM_NAME = 'test'
@ -21,7 +22,7 @@ class DropboxHookTests(WebhookTestCase):
self.subscribe(self.test_user, self.STREAM_NAME) self.subscribe(self.test_user, self.STREAM_NAME)
get_params = {'stream_name': self.STREAM_NAME, get_params = {'stream_name': self.STREAM_NAME,
'challenge': '9B2SVL4orbt5DxLMqJHI6pOTipTqingt2YFMIO0g06E', 'challenge': '9B2SVL4orbt5DxLMqJHI6pOTipTqingt2YFMIO0g06E',
'api_key': self.test_user.api_key} 'api_key': get_api_key(self.test_user)}
result = self.client_get(self.url, get_params) result = self.client_get(self.url, get_params)
self.assert_in_response('9B2SVL4orbt5DxLMqJHI6pOTipTqingt2YFMIO0g06E', result) self.assert_in_response('9B2SVL4orbt5DxLMqJHI6pOTipTqingt2YFMIO0g06E', result)

View File

@ -3,6 +3,7 @@ from typing import Dict, Optional
import ujson import ujson
from zerver.lib.test_classes import WebhookTestCase from zerver.lib.test_classes import WebhookTestCase
from zerver.lib.users import get_api_key
from zerver.lib.webhooks.git import COMMITS_LIMIT from zerver.lib.webhooks.git import COMMITS_LIMIT
from zerver.models import Message from zerver.models import Message
@ -39,7 +40,7 @@ class GithubV1HookTests(WebhookTestCase):
self.assertEqual(prior_count, after_count) self.assertEqual(prior_count, after_count)
def get_body(self, fixture_name: str) -> Dict[str, str]: def get_body(self, fixture_name: str) -> Dict[str, str]:
api_key = self.test_user.api_key api_key = get_api_key(self.test_user)
data = ujson.loads(self.webhook_fixture_data(self.FIXTURE_DIR_NAME, 'v1_' + fixture_name)) data = ujson.loads(self.webhook_fixture_data(self.FIXTURE_DIR_NAME, 'v1_' + fixture_name))
data.update({'email': self.TEST_USER_EMAIL, data.update({'email': self.TEST_USER_EMAIL,
'api-key': api_key, 'api-key': api_key,
@ -164,7 +165,7 @@ class GithubV2HookTests(WebhookTestCase):
self.assertEqual(prior_count, after_count) self.assertEqual(prior_count, after_count)
def get_body(self, fixture_name: str) -> Dict[str, str]: def get_body(self, fixture_name: str) -> Dict[str, str]:
api_key = self.test_user.api_key api_key = get_api_key(self.test_user)
data = ujson.loads(self.webhook_fixture_data(self.FIXTURE_DIR_NAME, 'v2_' + fixture_name)) data = ujson.loads(self.webhook_fixture_data(self.FIXTURE_DIR_NAME, 'v2_' + fixture_name))
data.update({'email': self.TEST_USER_EMAIL, data.update({'email': self.TEST_USER_EMAIL,
'api-key': api_key, 'api-key': api_key,

View File

@ -1,13 +1,14 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from zerver.lib.test_classes import WebhookTestCase from zerver.lib.test_classes import WebhookTestCase
from zerver.lib.users import get_api_key
class JiraHookTests(WebhookTestCase): class JiraHookTests(WebhookTestCase):
STREAM_NAME = 'jira' STREAM_NAME = 'jira'
URL_TEMPLATE = u"/api/v1/external/jira?api_key={api_key}&stream={stream}" URL_TEMPLATE = u"/api/v1/external/jira?api_key={api_key}&stream={stream}"
def test_custom_stream(self) -> None: def test_custom_stream(self) -> None:
api_key = self.test_user.api_key api_key = get_api_key(self.test_user)
url = "/api/v1/external/jira?api_key=%s&stream=jira_custom" % (api_key,) url = "/api/v1/external/jira?api_key=%s&stream=jira_custom" % (api_key,)
msg = self.send_json_payload(self.test_user, msg = self.send_json_payload(self.test_user,
url, url,

View File

@ -3,6 +3,7 @@ from typing import Any
from zerver.lib.initial_password import initial_password from zerver.lib.initial_password import initial_password
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
from zerver.lib.users import get_api_key
class Command(ZulipBaseCommand): class Command(ZulipBaseCommand):
help = "Print the initial password and API key for accounts as created by populate_db" help = "Print the initial password and API key for accounts as created by populate_db"
@ -21,4 +22,5 @@ class Command(ZulipBaseCommand):
if '@' not in email: if '@' not in email:
print('ERROR: %s does not look like an email address' % (email,)) print('ERROR: %s does not look like an email address' % (email,))
continue continue
print(self.fmt % (email, initial_password(email), self.get_user(email, realm).api_key)) user = self.get_user(email, realm)
print(self.fmt % (email, initial_password(email), get_api_key(user)))