Skip to content

Commit e6de565

Browse files
padthaitofuhotadrianmo
authored andcommitted
feature-password-group-swift (#37)
* implement password_group * implement password_group tests * implement password_group tests * some delete actions in the API return HTTP/1.1 204 No Content * import password_group * Update Password Group (Swift) support * Fix various errors from PR #37
1 parent 3e1d68b commit e6de565

File tree

7 files changed

+242
-7
lines changed

7 files changed

+242
-7
lines changed

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ The following table shows the supported endpoints per API version.
205205
+--------------------------+---------+---------+
206206
| Authentication Provider | ~ | ~ |
207207
+--------------------------+---------+---------+
208-
| Password Group (Swift) | | |
208+
| Password Group (Swift) | | |
209209
+--------------------------+---------+---------+
210210
| Secret Key |||
211211
+--------------------------+---------+---------+

ecsclient/baseclient.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ def _request(self, url, json_payload='{}', http_verb='GET', params=None):
164164
timeout=self.request_timeout,
165165
params=params)
166166

167-
if req.status_code != 200:
167+
# Because some delete actions in the API return HTTP/1.1 204 No Content
168+
if not (200 <= req.status_code < 300):
168169
log.error("Status code NOT OK")
169170
raise ECSClientException.from_response(req)
170171
try:
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# coding=utf-8
2+
import logging
3+
4+
log = logging.getLogger(__name__)
5+
6+
7+
class PasswordGroup(object):
8+
def __init__(self, connection):
9+
"""
10+
Initialize a new instance
11+
"""
12+
self.conn = connection
13+
14+
def get(self, user_id, namespace=None):
15+
"""
16+
Gets all Swift user groups for a specified user identifier.
17+
If namespace is provided then returns only groups for the specified namespace.
18+
19+
Required role(s):
20+
21+
SYSTEM_ADMIN
22+
SYSTEM_MONITOR
23+
NAMESPACE_ADMIN
24+
25+
Example JSON result from the API:
26+
27+
{
28+
"groups_list": [
29+
"admin"
30+
]
31+
}
32+
33+
:param user_id: Username for which group names should be returned.
34+
:param namespace: Namespace limiting returned groups (optional).
35+
"""
36+
msg = "Getting Swift groups for user '{}'".format(user_id)
37+
url = 'object/user-password/{}'.format(user_id)
38+
39+
if namespace:
40+
url += '/{}'.format(namespace)
41+
msg += " in namespace '{}'".format(namespace)
42+
43+
log.info(msg)
44+
return self.conn.get(url=url)
45+
46+
def create(self, user_id, password, groups_list, namespace=None):
47+
"""
48+
Creates password and group for a specific user.
49+
50+
Required role(s):
51+
52+
SYSTEM_ADMIN
53+
NAMESPACE_ADMIN
54+
55+
There is no response body for this call
56+
57+
Expect: HTTP/1.1 200 OK
58+
59+
:param user_id: Valid user identifier to create a key for. If not provided, the authenticated
60+
user will be used instead.
61+
:param namespace: Namespace for the user if the user belongs to a namespace.
62+
:param password: Swift password associated with this user.
63+
:param groups_list: List of Swift groups with which to associate this user. If user is a member
64+
of the "admin" group, user will be able to perform all container operations.
65+
If a member of any other group, authorization will depend on the access that is set on the container.
66+
"""
67+
68+
url = 'object/user-password/{}'.format(user_id)
69+
msg = "Creating Swift password for user '{}' in namespace '{}'".format(user_id, namespace)
70+
71+
payload = {'namespace': namespace,
72+
'password': password,
73+
'groups_list': groups_list}
74+
75+
log.info(msg)
76+
return self.conn.put(url, json_payload=payload)
77+
78+
def update(self, user_id, password, groups_list, namespace=None):
79+
"""
80+
Updates password and group information for a specific user identifier.
81+
82+
Required role(s):
83+
84+
SYSTEM_ADMIN
85+
NAMESPACE_ADMIN
86+
87+
There is no response body for this call
88+
89+
Expect: HTTP/1.1 200 OK
90+
91+
:param user_id: Valid user identifier to create a key for. If not provided,
92+
the authenticated user will be used instead.
93+
:param namespace: Namespace for the user if the user belongs to a namespace.
94+
:param password: Swift password associated with this user.
95+
:param groups_list: List of Swift groups with which to associate this user. If user is a member
96+
of the "admin" group, user will be able to perform all container operations.
97+
If a member of any other group, authorization will depend on the access that is set on the container.
98+
"""
99+
100+
url = 'object/user-password/{}'.format(user_id)
101+
msg = "Updating Swift password for user '{}' in namespace '{}'".format(user_id, namespace)
102+
103+
payload = {'namespace': namespace,
104+
'password': password,
105+
'groups_list': groups_list}
106+
107+
log.info(msg)
108+
return self.conn.post(url, json_payload=payload)
109+
110+
def delete(self, user_id, namespace=None):
111+
"""
112+
Deletes password group for a specified user.
113+
114+
Required role(s):
115+
116+
SYSTEM_ADMIN
117+
NAMESPACE_ADMIN
118+
119+
Example JSON result from the API:
120+
121+
There is no response body for this call
122+
123+
Expect: HTTP/1.1 204 No Response
124+
125+
:param user_id: Valid user identifier to get delete the keys from.
126+
:param namespace: Namespace for the user if the user belongs to a namespace.
127+
"""
128+
129+
url = 'object/user-password/{}/deactivate'.format(user_id)
130+
msg = "Deleting Swift password for user '{}' in namespace '{}'".format(user_id, namespace)
131+
132+
payload = {'namespace': namespace}
133+
134+
log.info(msg)
135+
return self.conn.post(url, json_payload=payload)

ecsclient/schemas.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,3 +768,21 @@
768768
"mgmt_user_info"
769769
]
770770
}
771+
772+
GROUP_LIST = {
773+
"type": "object",
774+
"properties": {
775+
"groups_list": {
776+
"type": "array",
777+
"items": {
778+
"type": "string",
779+
"minLength": 1
780+
},
781+
"minItems": 1,
782+
"uniqueItems": True
783+
}
784+
},
785+
"required": [
786+
"groups_list"
787+
]
788+
}

ecsclient/v3/client.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import logging
22

33
from ecsclient import baseclient
4-
from ecsclient.v3.configuration import certificate, configuration_properties, licensing, feature, syslog, snmp
4+
from ecsclient.v3.configuration import certificate, configuration_properties, \
5+
licensing, feature, syslog, snmp
56
from ecsclient.v3.cas import cas
67
from ecsclient.v3.metering import billing
78
from ecsclient.v3.monitoring import capacity, dashboard, events
89
from ecsclient.v3.multitenancy import namespace
910
from ecsclient.v3.geo_replication import replication_group, temporary_failed_zone
10-
from ecsclient.v2.provisioning import base_url, bucket, data_store, storage_pool, virtual_data_center, node
11-
from ecsclient.v3.user_management import authentication_provider, management_user, object_user, secret_key
11+
from ecsclient.v2.provisioning import base_url, bucket, data_store, storage_pool, \
12+
virtual_data_center, node
13+
from ecsclient.v3.user_management import authentication_provider, management_user, \
14+
object_user, secret_key, password_group
1215
from ecsclient.v3.other import user_info
1316

1417
# Initialize logger
@@ -68,7 +71,7 @@ def __init__(self, *args, **kwargs):
6871

6972
# User Management
7073
self.authentication_provider = authentication_provider.AuthenticationProvider(self)
71-
# TODO: self.password_group = password_group.PasswordGroup(self)
74+
self.password_group = password_group.PasswordGroup(self)
7275
self.secret_key = secret_key.SecretKey(self)
7376
self.management_user = management_user.ManagementUser(self)
7477
self.object_user = object_user.ObjectUser(self)
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from ecsclient.common.user_management import authentication_provider, secret_key, \
2-
management_user, object_user
2+
password_group, management_user, object_user
33

44
authentication_provider = authentication_provider
55
secret_key = secret_key
6+
password_group = password_group
67
management_user = management_user
78
object_user = object_user
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import time
2+
3+
from ecsclient import schemas
4+
from ecsclient.common.exceptions import ECSClientException
5+
from tests import functional
6+
7+
8+
class TestPasswordGroup(functional.BaseTestCase):
9+
def __init__(self, *args, **kwargs):
10+
super(TestPasswordGroup, self).__init__(*args, **kwargs)
11+
self.namespace_1 = "functional-tests-namespace-%s" % int(time.time())
12+
self.object_user_1 = "functional-tests-objectuser-%s" % int(time.time())
13+
self.object_user_2 = self.object_user_1 + '_second'
14+
self.password_1 = "fake-password-123"
15+
self.password_2 = "fake-password-456"
16+
self.groups_list_1 = ['admin']
17+
self.groups_list_2 = ['admin', 'user']
18+
19+
def setUp(self):
20+
super(TestPasswordGroup, self).setUp()
21+
self.client.namespace.create(self.namespace_1)
22+
self.client.object_user.create(self.object_user_1, self.namespace_1)
23+
self.client.password_group.create(self.object_user_1,
24+
self.password_1,
25+
self.groups_list_1,
26+
namespace=self.namespace_1)
27+
28+
def tearDown(self):
29+
super(TestPasswordGroup, self).tearDown()
30+
for object_user in [self.object_user_1,
31+
self.object_user_2]:
32+
try:
33+
self.client.object_user.delete(object_user)
34+
except ECSClientException:
35+
pass
36+
self.client.namespace.delete(self.namespace_1)
37+
38+
def test_password_group_create_for_user(self):
39+
self.client.password_group.create(user_id=self.object_user_2,
40+
namespace=self.namespace_1,
41+
password=self.password_2,
42+
groups_list=self.groups_list_2)
43+
44+
response = self.client.password_group.get(user_id=self.object_user_2,
45+
namespace=self.namespace_1)
46+
47+
self.assertValidSchema(response, schemas.GROUP_LIST)
48+
self.assertEqual(response['groups_list'], self.groups_list_2)
49+
50+
def test_password_group_get_by_user(self):
51+
response = self.client.password_group.get(user_id=self.object_user_1,
52+
namespace=self.namespace_1)
53+
54+
self.assertValidSchema(response, schemas.GROUP_LIST)
55+
self.assertEqual(response['groups_list'], self.groups_list_1)
56+
57+
def test_password_group_update_for_user(self):
58+
self.assertNotEqual(self.groups_list_1, self.groups_list_2)
59+
60+
response = self.client.password_group.get(user_id=self.object_user_1,
61+
namespace=self.namespace_1)
62+
self.assertEqual(response['groups_list'], self.groups_list_1)
63+
64+
self.client.password_group.update(user_id=self.object_user_1,
65+
namespace=self.namespace_1,
66+
password=self.password_2,
67+
groups_list=self.groups_list_2)
68+
69+
response = self.client.password_group.get(user_id=self.object_user_1,
70+
namespace=self.namespace_1)
71+
self.assertEqual(response['groups_list'], self.groups_list_2)
72+
73+
def test_password_group_delete_for_user(self):
74+
self.client.password_group.delete(user_id=self.object_user_1,
75+
namespace=self.namespace_1)
76+
f = self.client.password_group.get
77+
self.assertRaises(ECSClientException, f, self.object_user_1)

0 commit comments

Comments
 (0)