Skip to content
This repository was archived by the owner on Oct 15, 2020. It is now read-only.

Commit e29f507

Browse files
Merge pull request #335 from HewlettPackard/enhancement/#273-324-enabling-authentication-using-sessionID
adding support for authentication using sessionID
2 parents a958171 + a6a9648 commit e29f507

File tree

8 files changed

+195
-13
lines changed

8 files changed

+195
-13
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ Added endpoints-support.md to track the supported and tested endpoints for the d
66
- Login details
77

88
#### Bug fixes & Enhancements
9+
- [#273](https://github.com/HewlettPackard/python-hpOneView/issues/273) OneViewClient doesn't allow using a token (sessionId)
910
- [#317](https://github.com/HewlettPackard/python-hpOneView/issues/317) Resource "Roles" should be under "Security" instead of "Uncategorized"
1011
- [#320](https://github.com/HewlettPackard/python-hpOneView/issues/320) Issue with pickling HPOneViewException
12+
- [#324](https://github.com/HewlettPackard/python-hpOneView/issues/324) Is it possible to login with session token?
1113
- [#330](https://github.com/HewlettPackard/python-hpOneView/issues/330) Remove unused/legacy code from connection.py
1214

1315
# v4.2.0
@@ -128,7 +130,7 @@ Bugfixes and corrections.
128130
- Developer friendly interface
129131
- Standardization for building new endpoint clients
130132
- Core client implementation
131-
- Support for Python’s logging library
133+
- Support for Python’s logging library
132134
- Added possibility to load connection settings from configuration file
133135
- Simple access to OneView API endpoints through OneViewClient module
134136
3. Added developer-focused samples

README.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ Connection properties for accessing the OneView appliance can be set in a JSON f
6969
Before running the samples or your own scripts, you must create the JSON file.
7070
An example can be found at: [OneView configuration sample](examples/config-rename.json).
7171

72+
Note: If you have an active and valid login session and want to use it, define the sessionID in the Credentials. When sessionID is defined, you can remove username and password from your JSON (they will be disregarded anyway).
73+
7274
Once you have created the JSON file, you can initialize the OneViewClient:
7375

7476
```python
@@ -84,8 +86,12 @@ Configuration can also be stored in environment variables:
8486
```bash
8587
# Required
8688
export ONEVIEWSDK_IP='172.16.102.82'
89+
8790
export ONEVIEWSDK_USERNAME='Administrator'
8891
export ONEVIEWSDK_PASSWORD='secret123'
92+
# Or sessionID
93+
export ONEVIEWSDK_SESSIONID='123'
94+
8995

9096
# Optional
9197
export ONEVIEWSDK_API_VERSION='300'
@@ -95,6 +101,8 @@ export ONEVIEWSDK_PROXY='<proxy_host>:<proxy_port>'
95101

96102
:lock: Tip: Make sure no unauthorized person has access to the environment variables, since the password is stored in clear-text.
97103

104+
Note: If you have an active and valid login session and want to use it, define the ONEVIEWSDK_SESSIONID. When a sessionID is defined, it will be used for authentication (username and password will be ignored in this case).
105+
98106
Once you have defined the environment variables, you can initialize the OneViewClient using the following code snippet:
99107

100108
```python
@@ -103,7 +111,8 @@ oneview_client = OneViewClient.from_environment_variables()
103111

104112
### Dictionary
105113

106-
You can also set the configuration using a dictionary:
114+
You can also set the configuration using a dictionary. As described above, for authentication you can use username/password:
115+
107116

108117
```python
109118
config = {
@@ -113,6 +122,18 @@ config = {
113122
"password": "secret123"
114123
}
115124
}
125+
```
126+
127+
or if you have an active and valid login session and want to use it, define the sessionID in the Credentials:
128+
129+
130+
```python
131+
config = {
132+
"ip": "172.16.102.82",
133+
"credentials": {
134+
"sessionID": "123"
135+
}
136+
}
116137

117138
oneview_client = OneViewClient(config)
118139
```

examples/config-rename.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"credentials": {
66
"userName": "administrator",
77
"authLoginDomain": "",
8-
"password": ""
8+
"password": "",
9+
"sessionID": ""
910
},
1011
"enclosure_hostname": "172.18.1.13",
1112
"enclosure_username": "",

examples/connections.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,18 @@
2626
from hpOneView.exceptions import HPOneViewException
2727
from config_loader import try_load_from_file
2828

29+
# You can use username/password or sessionID for authentication.
30+
# Be sure to inform a valid and active sessionID.
2931
config = {
30-
"ip": "172.16.102.59",
32+
"ip": "<oneview_ip>",
3133
"credentials": {
32-
"userName": "administrator",
33-
"password": ""
34+
"userName": "<username>",
35+
"password": "<password>",
36+
"sessionID": "<sessionID>"
3437
}
3538
}
3639

40+
3741
# Try load config from a file (if there is a config file)
3842
config = try_load_from_file(config)
3943

hpOneView/connection.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,12 @@ def login(self, cred, verbose=False):
439439

440440
self._cred = cred
441441
try:
442-
task, body = self.post(uri['loginSessions'], self._cred)
442+
if self._cred.get("sessionID"):
443+
self.set_session_id(self._cred["sessionID"])
444+
task, body = self.put(uri['loginSessions'], None)
445+
else:
446+
self._cred.pop("sessionID", None)
447+
task, body = self.post(uri['loginSessions'], self._cred)
443448
except HPOneViewException:
444449
logger.exception('Login failed')
445450
raise

hpOneView/oneview_client.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ def from_environment_variables(cls):
215215
Construct OneViewClient using environment variables.
216216
217217
Allowed variables: ONEVIEWSDK_IP (required), ONEVIEWSDK_USERNAME (required), ONEVIEWSDK_PASSWORD (required),
218-
ONEVIEWSDK_AUTH_LOGIN_DOMAIN, ONEVIEWSDK_API_VERSION, ONEVIEWSDK_IMAGE_STREAMER_IP, and ONEVIEWSDK_PROXY.
218+
ONEVIEWSDK_AUTH_LOGIN_DOMAIN, ONEVIEWSDK_API_VERSION, ONEVIEWSDK_IMAGE_STREAMER_IP, ONEVIEWSDK_SESSIONID and ONEVIEWSDK_PROXY.
219219
220220
Returns:
221221
OneViewClient:
@@ -227,11 +227,12 @@ def from_environment_variables(cls):
227227
auth_login_domain = os.environ.get('ONEVIEWSDK_AUTH_LOGIN_DOMAIN', '')
228228
password = os.environ.get('ONEVIEWSDK_PASSWORD', '')
229229
proxy = os.environ.get('ONEVIEWSDK_PROXY', '')
230+
sessionID = os.environ.get('ONEVIEWSDK_SESSIONID', '')
230231

231232
config = dict(ip=ip,
232233
image_streamer_ip=image_streamer_ip,
233234
api_version=api_version,
234-
credentials=dict(userName=username, authLoginDomain=auth_login_domain, password=password),
235+
credentials=dict(userName=username, authLoginDomain=auth_login_domain, password=password, sessionID=sessionID),
235236
proxy=proxy)
236237

237238
return cls(config)

tests/unit/test_connection.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,44 @@ def test_login_with_exception_in_post(self, mock_post, mock_get):
884884

885885
self.assertRaises(HPOneViewException, self.connection.login, {})
886886

887+
@patch.object(connection, 'get')
888+
@patch.object(connection, 'put')
889+
def test_login_sessionID(self, mock_put, mock_get):
890+
mock_get.side_effect = [{'minimumVersion': 300, 'currentVersion': 400}]
891+
mock_put.return_value = {'cat': 'task'}, {'sessionID': '123'}
892+
893+
self.connection.login({"sessionID": "123"})
894+
895+
self.assertEqual(self.connection.get_session_id(), '123')
896+
self.assertEqual(self.connection.get_session(), True)
897+
898+
@patch.object(connection, 'get')
899+
@patch.object(connection, 'put')
900+
def test_login_username_password_sessionID(self, mock_put, mock_get):
901+
mock_get.side_effect = [{'minimumVersion': 300, 'currentVersion': 400}]
902+
mock_put.return_value = {'cat': 'task'}, {'sessionID': '123'}
903+
904+
self.connection.login({"userName": "administrator", "password": "", "sessionID": "123"})
905+
906+
self.assertEqual(self.connection.get_session_id(), '123')
907+
self.assertEqual(self.connection.get_session(), True)
908+
909+
@patch.object(connection, 'get')
910+
@patch.object(connection, 'put')
911+
def test_login_with_exception_in_put(self, mock_put, mock_get):
912+
mock_get.side_effect = [{'minimumVersion': 300, 'currentVersion': 400}]
913+
mock_put.side_effect = HPOneViewException("Failed")
914+
915+
self.assertRaises(HPOneViewException, self.connection.login, {"sessionID": "123"})
916+
917+
@patch.object(connection, 'get')
918+
@patch.object(connection, 'put')
919+
def test_login_with_exception_in_put_username_password_sessionID(self, mock_put, mock_get):
920+
mock_get.side_effect = [{'minimumVersion': 300, 'currentVersion': 400}]
921+
mock_put.side_effect = HPOneViewException("Failed")
922+
923+
self.assertRaises(HPOneViewException, self.connection.login, {"userName": "administrator", "password": "", "sessionID": "123"})
924+
887925
@patch.object(connection, 'get')
888926
def test_validate_version_exceeding_minimum(self, mock_get):
889927
self.connection._apiVersion = 300

tests/unit/test_oneview_client.py

Lines changed: 114 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@
8686
'ONEVIEWSDK_PASSWORD': 'secret123'
8787
}
8888

89+
OS_ENVIRON_CONFIG_MINIMAL_WITH_SESSIONID = {
90+
'ONEVIEWSDK_IP': '172.16.100.199',
91+
'ONEVIEWSDK_SESSIONID': '123'
92+
}
93+
8994
OS_ENVIRON_CONFIG_FULL = {
9095
'ONEVIEWSDK_IP': '172.16.100.199',
9196
'ONEVIEWSDK_IMAGE_STREAMER_IP': '172.172.172.172',
@@ -96,6 +101,16 @@
96101
'ONEVIEWSDK_PROXY': '172.16.100.195:9999'
97102
}
98103

104+
OS_ENVIRON_CONFIG_FULL_WITH_SESSIONID = {
105+
'ONEVIEWSDK_IP': '172.16.100.199',
106+
'ONEVIEWSDK_IMAGE_STREAMER_IP': '172.172.172.172',
107+
'ONEVIEWSDK_USERNAME': 'admin',
108+
'ONEVIEWSDK_PASSWORD': 'secret123',
109+
'ONEVIEWSDK_SESSIONID': '123',
110+
'ONEVIEWSDK_API_VERSION': '201',
111+
'ONEVIEWSDK_PROXY': '172.16.100.195:9999'
112+
}
113+
99114

100115
class OneViewClientTest(unittest.TestCase):
101116
def __mock_file_open(self, json_config_content):
@@ -147,6 +162,39 @@ def test_from_json_file(self, mock_open, mock_login):
147162
self.assertIsInstance(oneview_client, OneViewClient)
148163
self.assertEqual("172.16.102.59", oneview_client.connection.get_host())
149164

165+
@mock.patch.object(connection, 'login')
166+
@mock.patch(mock_builtin('open'))
167+
def test_from_json_file_with_sessionID(self, mock_open, mock_login):
168+
json_config_content = u"""{
169+
"ip": "172.16.102.59",
170+
"credentials": {
171+
"userName": "administrator",
172+
"authLoginDomain": "",
173+
"password": "",
174+
"sessionID": "123"
175+
}
176+
}"""
177+
mock_open.return_value = self.__mock_file_open(json_config_content)
178+
oneview_client = OneViewClient.from_json_file("config.json")
179+
180+
self.assertIsInstance(oneview_client, OneViewClient)
181+
self.assertEqual("172.16.102.59", oneview_client.connection.get_host())
182+
183+
@mock.patch.object(connection, 'login')
184+
@mock.patch(mock_builtin('open'))
185+
def test_from_json_file_with_only_sessionID(self, mock_open, mock_login):
186+
json_config_content = u"""{
187+
"ip": "172.16.102.59",
188+
"credentials": {
189+
"sessionID": "123"
190+
}
191+
}"""
192+
mock_open.return_value = self.__mock_file_open(json_config_content)
193+
oneview_client = OneViewClient.from_json_file("config.json")
194+
195+
self.assertIsInstance(oneview_client, OneViewClient)
196+
self.assertEqual("172.16.102.59", oneview_client.connection.get_host())
197+
150198
@mock.patch.object(connection, 'login')
151199
@mock.patch(mock_builtin('open'))
152200
def test_default_api_version(self, mock_open, mock_login):
@@ -190,7 +238,21 @@ def test_from_minimal_environment_variables(self, mock_set_proxy, mock_login):
190238

191239
mock_login.assert_called_once_with(dict(userName='admin',
192240
password='secret123',
193-
authLoginDomain=''))
241+
authLoginDomain='',
242+
sessionID=''))
243+
mock_set_proxy.assert_not_called()
244+
self.assertEqual(300, oneview_client.connection._apiVersion)
245+
246+
@mock.patch.object(connection, 'login')
247+
@mock.patch.object(connection, 'set_proxy')
248+
@mock.patch.dict('os.environ', OS_ENVIRON_CONFIG_MINIMAL_WITH_SESSIONID)
249+
def test_from_minimal_environment_variables_with_sessionID(self, mock_set_proxy, mock_login):
250+
oneview_client = OneViewClient.from_environment_variables()
251+
252+
mock_login.assert_called_once_with(dict(userName='',
253+
password='',
254+
authLoginDomain='',
255+
sessionID='123'))
194256
mock_set_proxy.assert_not_called()
195257
self.assertEqual(300, oneview_client.connection._apiVersion)
196258

@@ -202,13 +264,30 @@ def test_from_full_environment_variables(self, mock_set_proxy, mock_login):
202264

203265
mock_login.assert_called_once_with(dict(userName='admin',
204266
password='secret123',
205-
authLoginDomain='authdomain'))
267+
authLoginDomain='authdomain',
268+
sessionID=''))
206269
mock_set_proxy.assert_called_once_with('172.16.100.195', 9999)
207270

208271
self.assertEqual(201, oneview_client.connection._apiVersion)
209272
self.assertEqual(oneview_client.create_image_streamer_client().connection.get_host(),
210273
OS_ENVIRON_CONFIG_FULL['ONEVIEWSDK_IMAGE_STREAMER_IP'])
211274

275+
@mock.patch.object(connection, 'login')
276+
@mock.patch.object(connection, 'set_proxy')
277+
@mock.patch.dict('os.environ', OS_ENVIRON_CONFIG_FULL_WITH_SESSIONID)
278+
def test_from_full_environment_variables_with_sessionID(self, mock_set_proxy, mock_login):
279+
oneview_client = OneViewClient.from_environment_variables()
280+
281+
mock_login.assert_called_once_with(dict(userName='admin',
282+
password='secret123',
283+
authLoginDomain='',
284+
sessionID='123'))
285+
mock_set_proxy.assert_called_once_with('172.16.100.195', 9999)
286+
287+
self.assertEqual(201, oneview_client.connection._apiVersion)
288+
self.assertEqual(oneview_client.create_image_streamer_client().connection.get_host(),
289+
OS_ENVIRON_CONFIG_FULL_WITH_SESSIONID['ONEVIEWSDK_IMAGE_STREAMER_IP'])
290+
212291
@mock.patch.dict('os.environ', OS_ENVIRON_CONFIG_FULL)
213292
@mock.patch.object(OneViewClient, '__init__')
214293
def test_from_environment_variables_is_passing_right_arguments_to_the_constructor(self, mock_cls):
@@ -219,9 +298,40 @@ def test_from_environment_variables_is_passing_right_arguments_to_the_constructo
219298
'ip': '172.16.100.199',
220299
'image_streamer_ip': '172.172.172.172',
221300
'credentials':
222-
{'password': 'secret123',
301+
{'userName': 'admin',
302+
'password': 'secret123',
223303
'authLoginDomain': 'authdomain',
224-
'userName': 'admin'}})
304+
'sessionID': ''}})
305+
306+
@mock.patch.dict('os.environ', OS_ENVIRON_CONFIG_FULL_WITH_SESSIONID)
307+
@mock.patch.object(OneViewClient, '__init__')
308+
def test_from_environment_variables_is_passing_right_arguments_to_the_constructor_with_sessionID(self, mock_cls):
309+
mock_cls.return_value = None
310+
OneViewClient.from_environment_variables()
311+
mock_cls.assert_called_once_with({'api_version': 201,
312+
'proxy': '172.16.100.195:9999',
313+
'ip': '172.16.100.199',
314+
'image_streamer_ip': '172.172.172.172',
315+
'credentials':
316+
{'userName': 'admin',
317+
'password': 'secret123',
318+
'authLoginDomain': '',
319+
'sessionID': '123'}})
320+
321+
@mock.patch.dict('os.environ', OS_ENVIRON_CONFIG_MINIMAL_WITH_SESSIONID)
322+
@mock.patch.object(OneViewClient, '__init__')
323+
def test_from_environment_variables_is_passing_right_arguments_to_the_constructor_with_only_sessionID(self, mock_cls):
324+
mock_cls.return_value = None
325+
OneViewClient.from_environment_variables()
326+
mock_cls.assert_called_once_with({'api_version': 300,
327+
'proxy': '',
328+
'ip': '172.16.100.199',
329+
'image_streamer_ip': '',
330+
'credentials':
331+
{'userName': '',
332+
'password': '',
333+
'authLoginDomain': '',
334+
'sessionID': '123'}})
225335

226336
@mock.patch.object(connection, 'login')
227337
def test_create_image_streamer_client_without_image_streamer_ip(self, mock_login):

0 commit comments

Comments
 (0)