Skip to content

Commit 35b0bbd

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Implement CRUD operations for Mapping objects"
2 parents 0cb204e + 30b0a41 commit 35b0bbd

4 files changed

Lines changed: 515 additions & 0 deletions

File tree

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# Copyright 2014 CERN
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
#
15+
16+
"""Identity v3 federation mapping action implementations"""
17+
18+
import json
19+
import logging
20+
21+
from cliff import command
22+
from cliff import lister
23+
from cliff import show
24+
import six
25+
26+
from openstackclient.common import exceptions
27+
from openstackclient.common import utils
28+
29+
30+
class _RulesReader(object):
31+
"""Helper class capable of reading rules from files"""
32+
33+
def _read_rules(self, path):
34+
"""Read and parse rules from path
35+
36+
Expect the file to contain a valid JSON structure.
37+
38+
:param path: path to the file
39+
:return: loaded and valid dictionary with rules
40+
:raises exception.CommandError: In case the file cannot be
41+
accessed or the content is not a valid JSON.
42+
43+
Example of the content of the file:
44+
[
45+
{
46+
"local": [
47+
{
48+
"group": {
49+
"id": "85a868"
50+
}
51+
}
52+
],
53+
"remote": [
54+
{
55+
"type": "orgPersonType",
56+
"any_one_of": [
57+
"Employee"
58+
]
59+
},
60+
{
61+
"type": "sn",
62+
"any_one_of": [
63+
"Young"
64+
]
65+
}
66+
]
67+
}
68+
]
69+
70+
"""
71+
blob = utils.read_blob_file_contents(path)
72+
try:
73+
rules = json.loads(blob)
74+
except ValueError as e:
75+
raise exceptions.CommandError(
76+
'An error occurred when reading '
77+
'rules from file %s: %s' % (path, e))
78+
else:
79+
return rules
80+
81+
82+
class CreateMapping(show.ShowOne, _RulesReader):
83+
"""Create new federation mapping"""
84+
85+
log = logging.getLogger(__name__ + '.CreateMapping')
86+
87+
def get_parser(self, prog_name):
88+
parser = super(CreateMapping, self).get_parser(prog_name)
89+
parser.add_argument(
90+
'mapping',
91+
metavar='<name>',
92+
help='New mapping (must be unique)',
93+
)
94+
parser.add_argument(
95+
'--rules',
96+
metavar='<rules>', required=True,
97+
help='Filename with rules',
98+
)
99+
return parser
100+
101+
def take_action(self, parsed_args):
102+
self.log.debug('take_action(%s)' % parsed_args)
103+
identity_client = self.app.client_manager.identity
104+
105+
rules = self._read_rules(parsed_args.rules)
106+
mapping = identity_client.federation.mappings.create(
107+
mapping_id=parsed_args.mapping,
108+
rules=rules)
109+
110+
info = {}
111+
info.update(mapping._info)
112+
return zip(*sorted(six.iteritems(info)))
113+
114+
115+
class DeleteMapping(command.Command):
116+
"""Delete federation mapping"""
117+
118+
log = logging.getLogger(__name__ + '.DeleteMapping')
119+
120+
def get_parser(self, prog_name):
121+
parser = super(DeleteMapping, self).get_parser(prog_name)
122+
parser.add_argument(
123+
'mapping',
124+
metavar='<name>',
125+
help='Mapping to delete',
126+
)
127+
return parser
128+
129+
def take_action(self, parsed_args):
130+
self.log.debug('take_action(%s)' % parsed_args)
131+
identity_client = self.app.client_manager.identity
132+
133+
identity_client.federation.mappings.delete(parsed_args.mapping)
134+
return
135+
136+
137+
class ListMapping(lister.Lister):
138+
"""List federation mappings"""
139+
log = logging.getLogger(__name__ + '.ListMapping')
140+
141+
def take_action(self, parsed_args):
142+
self.log.debug('take_action(%s)' % parsed_args)
143+
# NOTE(marek-denis): Since rules can be long and tedious I have decided
144+
# to only list ids of the mappings. If somebody wants to check the
145+
# rules, (s)he should show specific ones.
146+
identity_client = self.app.client_manager.identity
147+
data = identity_client.federation.mappings.list()
148+
columns = ('ID',)
149+
items = [utils.get_item_properties(s, columns) for s in data]
150+
return (columns, items)
151+
152+
153+
class SetMapping(show.ShowOne, _RulesReader):
154+
"""Update federation mapping"""
155+
156+
log = logging.getLogger(__name__ + '.SetMapping')
157+
158+
def get_parser(self, prog_name):
159+
parser = super(SetMapping, self).get_parser(prog_name)
160+
parser.add_argument(
161+
'mapping',
162+
metavar='<name>',
163+
help='Mapping to update.',
164+
)
165+
parser.add_argument(
166+
'--rules',
167+
metavar='<rules>', required=True,
168+
help='Filename with rules',
169+
)
170+
return parser
171+
172+
def take_action(self, parsed_args):
173+
self.log.debug('take_action(%s)' % parsed_args)
174+
identity_client = self.app.client_manager.identity
175+
176+
rules = self._read_rules(parsed_args.rules)
177+
178+
mapping = identity_client.federation.mappings.update(
179+
mapping=parsed_args.mapping,
180+
rules=rules)
181+
182+
info = {}
183+
info.update(mapping._info)
184+
return zip(*sorted(six.iteritems(info)))
185+
186+
187+
class ShowMapping(show.ShowOne):
188+
"""Show federation mapping details"""
189+
190+
log = logging.getLogger(__name__ + '.ShowMapping')
191+
192+
def get_parser(self, prog_name):
193+
parser = super(ShowMapping, self).get_parser(prog_name)
194+
parser.add_argument(
195+
'mapping',
196+
metavar='<name>',
197+
help='Mapping to show',
198+
)
199+
return parser
200+
201+
def take_action(self, parsed_args):
202+
self.log.debug('take_action(%s)' % parsed_args)
203+
identity_client = self.app.client_manager.identity
204+
205+
mapping = identity_client.federation.mappings.get(parsed_args.mapping)
206+
207+
info = {}
208+
info.update(mapping._info)
209+
return zip(*sorted(six.iteritems(info)))

‎openstackclient/tests/identity/v3/fakes.py‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,65 @@
3838
'name': group_name,
3939
}
4040

41+
mapping_id = 'test_mapping'
42+
mapping_rules_file_path = '/tmp/path/to/file'
43+
# Copied from
44+
# (https://github.com/openstack/keystone/blob\
45+
# master/keystone/tests/mapping_fixtures.py
46+
EMPLOYEE_GROUP_ID = "0cd5e9"
47+
DEVELOPER_GROUP_ID = "xyz"
48+
MAPPING_RULES = [
49+
{
50+
"local": [
51+
{
52+
"group": {
53+
"id": EMPLOYEE_GROUP_ID
54+
}
55+
}
56+
],
57+
"remote": [
58+
{
59+
"type": "orgPersonType",
60+
"not_any_of": [
61+
"Contractor",
62+
"Guest"
63+
]
64+
}
65+
]
66+
}
67+
]
68+
69+
MAPPING_RULES_2 = [
70+
{
71+
"local": [
72+
{
73+
"group": {
74+
"id": DEVELOPER_GROUP_ID
75+
}
76+
}
77+
],
78+
"remote": [
79+
{
80+
"type": "orgPersonType",
81+
"any_one_of": [
82+
"Contractor"
83+
]
84+
}
85+
]
86+
}
87+
]
88+
89+
90+
MAPPING_RESPONSE = {
91+
"id": mapping_id,
92+
"rules": MAPPING_RULES
93+
}
94+
95+
MAPPING_RESPONSE_2 = {
96+
"id": mapping_id,
97+
"rules": MAPPING_RULES_2
98+
}
99+
41100
project_id = '8-9-64'
42101
project_name = 'beatles'
43102
project_description = 'Fab Four'
@@ -224,6 +283,8 @@ class FakeFederationManager(object):
224283
def __init__(self, **kwargs):
225284
self.identity_providers = mock.Mock()
226285
self.identity_providers.resource_class = fakes.FakeResource(None, {})
286+
self.mappings = mock.Mock()
287+
self.mappings.resource_class = fakes.FakeResource(None, {})
227288

228289

229290
class FakeFederatedClient(FakeIdentityv3Client):

0 commit comments

Comments
 (0)