Skip to content

Commit 907b22c

Browse files
committed
rotate key does now login with the corresponding access-key it should rotate
1 parent fe0472e commit 907b22c

File tree

3 files changed

+42
-54
lines changed

3 files changed

+42
-54
lines changed

app/core/core.py

+14-15
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import logging
2-
import sys
32
from typing import Optional, Callable
43

54
from app.aws import credentials, iam
65
from app.core import files
76
from app.core.config import Config, ProfileGroup
87
from app.core.result import Result
9-
from app.yubico import mfa
108
from app.gcp import login, config
9+
from app.yubico import mfa
1110

1211
logger = logging.getLogger('logsmith')
1312

@@ -130,25 +129,22 @@ def get_profile_group_list(self):
130129
def get_active_profile_color(self):
131130
return self.active_profile_group.color
132131

133-
def rotate_access_key(self, key_name: str) -> Result:
132+
def rotate_access_key(self, key_name: str, mfa_callback: Callable) -> Result:
134133
result = Result()
135134
logger.info('initiate key rotation')
135+
136+
logger.info('logout')
137+
self.logout()
138+
136139
logger.info('check access key')
137140
access_key_result = credentials.check_access_key(access_key=key_name)
138141
if not access_key_result.was_success:
139142
return access_key_result
140143

141-
logger.info(f'check if access key {key_name} is in use and can be rotated')
142-
if not self.active_profile_group or self.active_profile_group.get_access_key() != key_name:
143-
result = Result()
144-
result.error(f'Please login with a profile that uses \'{key_name}\' first')
145-
return result
146-
147-
logger.info('check session')
148-
check_session_result = credentials.check_session()
149-
if not check_session_result.was_success:
150-
check_session_result.error('Access Denied. Please log first')
151-
return check_session_result
144+
logger.info('fetch session')
145+
renew_session_result = self._renew_session(access_key=key_name, mfa_callback=mfa_callback)
146+
if not renew_session_result.was_success:
147+
return renew_session_result
152148

153149
logger.info('create key')
154150
user = credentials.get_user_name(key_name)
@@ -158,13 +154,16 @@ def rotate_access_key(self, key_name: str) -> Result:
158154

159155
logger.info('delete key')
160156
previous_access_key_id = credentials.get_access_key_id()
161-
iam.delete_iam_access_key(user, previous_access_key_id)
157+
delete_access_key_result = iam.delete_iam_access_key(user, previous_access_key_id)
158+
if not delete_access_key_result.was_success:
159+
return delete_access_key_result
162160

163161
logger.info('save key')
164162
credentials.set_access_key(key_name=key_name,
165163
key_id=create_access_key_result.payload['AccessKeyId'],
166164
key_secret=create_access_key_result.payload['SecretAccessKey'])
167165

166+
self.logout()
168167
result.set_success()
169168
return result
170169

app/gui/gui.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,16 @@
1010

1111
from app.core import files
1212
from app.core.config import Config, ProfileGroup
13+
from app.core.core import Core
1314
from app.core.result import Result
1415
from app.gui.access_key_dialog import SetKeyDialog
1516
from app.gui.assets import Assets
1617
from app.gui.config_dialog import ConfigDialog
1718
from app.gui.key_rotation_dialog import RotateKeyDialog
1819
from app.gui.log_dialog import LogDialog
19-
from app.gui.trayicon import SystemTrayIcon
20-
from app.core.core import Core
2120
from app.gui.mfa_dialog import MfaDialog
2221
from app.gui.repeater import Repeater
22+
from app.gui.trayicon import SystemTrayIcon
2323

2424
logger = logging.getLogger('logsmith')
2525

@@ -99,7 +99,7 @@ def set_access_key(self, key_name, key_id, access_key):
9999

100100
def rotate_access_key(self, key_name: str):
101101
logger.info('initiate key rotation')
102-
result = self.core.rotate_access_key(key_name=key_name)
102+
result = self.core.rotate_access_key(key_name=key_name, mfa_callback=self.show_mfa_token_fetch_dialog)
103103
if not self._check_and_signal_error(result):
104104
return
105105
self._signal('Success', 'key was rotated')

tests/test_core/test_core.py

+25-36
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# Show full diff in self.assertEqual.
1212
__import__('sys').modules['unittest.util']._MAX_LENGTH = 999999999
1313

14+
1415
class TestCore(TestCase):
1516
@classmethod
1617
def setUpClass(cls):
@@ -120,68 +121,56 @@ def test_login__logout_error(self, mock_credentials):
120121

121122
self.assertEqual(self.error_result, result)
122123

124+
@mock.patch('app.core.core.Core.logout')
123125
@mock.patch('app.core.core.credentials')
124-
def test_rotate_access_key__no_access_key(self, mock_credentials):
126+
def test_rotate_access_key__no_access_key(self, mock_credentials, mock_logout):
125127
mock_credentials.check_access_key.return_value = self.error_result
126-
result = self.core.rotate_access_key('rotate-this-key')
128+
mock_mfa_callback = Mock()
129+
result = self.core.rotate_access_key('rotate-this-key', mock_mfa_callback)
127130

128131
expected = [call.check_access_key(access_key='rotate-this-key')]
129132
self.assertEqual(expected, mock_credentials.mock_calls)
130133
self.assertEqual(self.error_result, result)
134+
self.assertEqual(1, mock_logout.call_count)
131135

136+
@mock.patch('app.core.core.Core._renew_session')
137+
@mock.patch('app.core.core.Core.logout')
132138
@mock.patch('app.core.core.iam')
133139
@mock.patch('app.core.core.credentials')
134-
def test_rotate_access_key__access_key_is_not_logged_in_and_cannot_be_rotated(self, mock_credentials, mock_iam):
140+
def test_rotate_access_key__successful_rotate(self, mock_credentials, mock_iam, mock_logout, mock_renew_session):
135141
mock_credentials.check_access_key.return_value = self.success_result
136142
mock_credentials.check_session.return_value = self.success_result
137143
mock_credentials.get_user_name.return_value = 'test-user'
138144
mock_credentials.get_access_key_id.return_value = '12345'
145+
mock_renew_session.return_value = self.success_result
139146

140147
access_key_result = Result()
141148
access_key_result.add_payload({'AccessKeyId': 12345, 'SecretAccessKey': 67890})
142149
access_key_result.set_success()
143150

144151
mock_iam.create_access_key.return_value = access_key_result
152+
mock_iam.delete_iam_access_key.return_value = self.success_result
145153

146-
result = self.core.rotate_access_key('rotate-this-key')
147-
148-
expected = [call.check_access_key(access_key='rotate-this-key')]
149-
self.assertEqual(expected, mock_credentials.mock_calls)
150-
151-
self.assertEqual(False, result.was_success)
152-
self.assertEqual(True, result.was_error)
153-
154-
@mock.patch('app.core.core.iam')
155-
@mock.patch('app.core.core.credentials')
156-
def test_rotate_access_key__successful_rotate(self, mock_credentials, mock_iam):
157-
mock_credentials.check_access_key.return_value = self.success_result
158-
mock_credentials.check_session.return_value = self.success_result
159-
mock_credentials.get_user_name.return_value = 'test-user'
160-
mock_credentials.get_access_key_id.return_value = '12345'
161-
162-
access_key_result = Result()
163-
access_key_result.add_payload({'AccessKeyId': 12345, 'SecretAccessKey': 67890})
164-
access_key_result.set_success()
165-
166-
mock_iam.create_access_key.return_value = access_key_result
154+
mock_mfa_callback = Mock()
155+
result = self.core.rotate_access_key('some-access-key', mock_mfa_callback)
167156

168-
# Login ino profile, then rotate the key
169-
self.core.active_profile_group = self.config.get_group('development')
170-
result = self.core.rotate_access_key('some-access-key')
157+
expected_credential_calls = [call.check_access_key(access_key='some-access-key'),
158+
# call.check_session(), # TODO can't make sure if the session is valid because there is only one "session"
159+
call.get_user_name('some-access-key'),
160+
call.get_access_key_id(),
161+
call.set_access_key(key_name='some-access-key', key_id=12345, key_secret=67890)]
162+
self.assertEqual(expected_credential_calls, mock_credentials.mock_calls)
171163

172-
expected = [call.check_access_key(access_key='some-access-key'),
173-
call.check_session(),
174-
call.get_user_name('some-access-key'),
175-
call.get_access_key_id(),
176-
call.set_access_key(key_name='some-access-key', key_id=12345, key_secret=67890)]
177-
self.assertEqual(expected, mock_credentials.mock_calls)
164+
renew_session_calls = [call(access_key='some-access-key', mfa_callback=mock_mfa_callback)]
165+
self.assertEqual(renew_session_calls, mock_renew_session.mock_calls)
178166

179-
expected = [call.create_access_key('test-user'),
180-
call.delete_iam_access_key('test-user', '12345')]
181-
self.assertEqual(expected, mock_iam.mock_calls)
167+
expected_iam_calls = [call.create_access_key('test-user'),
168+
call.delete_iam_access_key('test-user', '12345')]
169+
self.assertEqual(expected_iam_calls, mock_iam.mock_calls)
182170

183171
self.assertEqual(True, result.was_success)
184172
self.assertEqual(False, result.was_error)
173+
self.assertEqual(2, mock_logout.call_count)
185174

186175
def test_get_region__not_logged_in(self):
187176
region = self.core.get_region()

0 commit comments

Comments
 (0)