Merge "Introduce Castellan Credential Factory"
Jenkins authored 8 years ago
Gerrit Code Review committed 8 years ago
61 | 61 | |
62 | 62 | class ManagedObjectNotFoundError(CastellanException): |
63 | 63 | message = u._("Key not found, uuid: %(uuid)s") |
64 | ||
65 | ||
66 | class AuthTypeInvalidError(CastellanException): | |
67 | message = u._("Invalid auth_type was specified, auth_type: %(type)s") | |
68 | ||
69 | ||
70 | class InsufficientCredentialDataError(CastellanException): | |
71 | message = u._("Insufficient credential data was provided, either " | |
72 | "\"token\" must be set in the passed conf, or a context " | |
73 | "with an \"auth_token\" property must be passed.") |
0 | # Copyright (c) 2016 IBM | |
1 | # Licensed under the Apache License, Version 2.0 (the "License"); | |
2 | # you may not use this file except in compliance with the License. | |
3 | # You may obtain a copy of the License at | |
4 | # | |
5 | # http://www.apache.org/licenses/LICENSE-2.0 | |
6 | # | |
7 | # Unless required by applicable law or agreed to in writing, software | |
8 | # distributed under the License is distributed on an "AS IS" BASIS, | |
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
10 | # implied. | |
11 | # See the License for the specific language governing permissions and | |
12 | # limitations under the License. | |
13 | ||
14 | """ | |
15 | Common utilities for Castellan. | |
16 | """ | |
17 | ||
18 | from castellan.common.credentials import keystone_password | |
19 | from castellan.common.credentials import keystone_token | |
20 | from castellan.common.credentials import password | |
21 | from castellan.common.credentials import token | |
22 | from castellan.common import exception | |
23 | ||
24 | from oslo_config import cfg | |
25 | from oslo_log import log as logging | |
26 | ||
27 | ||
28 | LOG = logging.getLogger(__name__) | |
29 | ||
30 | credential_opts = [ | |
31 | # auth_type opt | |
32 | cfg.StrOpt('auth_type', default=None), | |
33 | ||
34 | # token opt | |
35 | cfg.StrOpt('token', default=None), | |
36 | ||
37 | # password opts | |
38 | cfg.StrOpt('username', default=None), | |
39 | cfg.StrOpt('password', default=None), | |
40 | ||
41 | # keystone credential opts | |
42 | cfg.StrOpt('user_id', default=None), | |
43 | cfg.StrOpt('user_domain_id', default=None), | |
44 | cfg.StrOpt('user_domain_name', default=None), | |
45 | cfg.StrOpt('trust_id', default=None), | |
46 | cfg.StrOpt('domain_id', default=None), | |
47 | cfg.StrOpt('domain_name', default=None), | |
48 | cfg.StrOpt('project_id', default=None), | |
49 | cfg.StrOpt('project_name', default=None), | |
50 | cfg.StrOpt('project_domain_id', default=None), | |
51 | cfg.StrOpt('project_domain_name', default=None), | |
52 | cfg.BoolOpt('reauthenticate', default=True) | |
53 | ] | |
54 | ||
55 | OPT_GROUP = 'key_manager' | |
56 | ||
57 | ||
58 | def credential_factory(conf=None, context=None): | |
59 | """This function provides a factory for credentials. | |
60 | ||
61 | It is used to create an appropriare credential object | |
62 | from a passed configuration. This should be called before | |
63 | making any calls to a key manager. | |
64 | ||
65 | :param conf: Configuration file which this factory method uses | |
66 | to generate a credential object. Note: In the future it will | |
67 | become a required field. | |
68 | :param context: Context used for authentication. It can be used | |
69 | in conjunction with the configuration file. If no conf is passed, | |
70 | then the context object will be converted to a KeystoneToken and | |
71 | returned. If a conf is passed then only the 'token' is grabbed from | |
72 | the context for the authentication types that require a token. | |
73 | :returns: A credential object used for authenticating with the | |
74 | Castellan key manager. Type of credential returned depends on | |
75 | config and/or context passed. | |
76 | """ | |
77 | if conf: | |
78 | conf.register_opts(credential_opts, group=OPT_GROUP) | |
79 | ||
80 | if conf.key_manager.auth_type == 'token': | |
81 | if conf.key_manager.token: | |
82 | auth_token = conf.key_manager.token | |
83 | elif context: | |
84 | auth_token = context.auth_token | |
85 | else: | |
86 | raise exception.InsufficientCredentialDataError() | |
87 | ||
88 | return token.Token(auth_token) | |
89 | ||
90 | elif conf.key_manager.auth_type == 'password': | |
91 | return password.Password( | |
92 | conf.key_manager.username, | |
93 | conf.key_manager.password) | |
94 | ||
95 | elif conf.key_manager.auth_type == 'keystone_password': | |
96 | return keystone_password.KeystonePassword( | |
97 | conf.key_manager.password, | |
98 | username=conf.key_manager.username, | |
99 | user_id=conf.key_manager.user_id, | |
100 | user_domain_id=conf.key_manager.user_domain_id, | |
101 | user_domain_name=conf.key_manager.user_domain_name, | |
102 | trust_id=conf.key_manager.trust_id, | |
103 | domain_id=conf.key_manager.domain_id, | |
104 | domain_name=conf.key_manager.domain_name, | |
105 | project_id=conf.key_manager.project_id, | |
106 | project_name=conf.key_manager.project_name, | |
107 | project_domain_id=conf.key_manager.domain_id, | |
108 | project_domain_name=conf.key_manager.domain_name, | |
109 | reauthenticate=conf.key_manager.reauthenticate) | |
110 | ||
111 | elif conf.key_manager.auth_type == 'keystone_token': | |
112 | if conf.key_manager.token: | |
113 | auth_token = conf.key_manager.token | |
114 | elif context: | |
115 | auth_token = context.auth_token | |
116 | else: | |
117 | raise exception.InsufficientCredentialDataError() | |
118 | ||
119 | return keystone_token.KeystoneToken( | |
120 | auth_token, | |
121 | trust_id=conf.key_manager.trust_id, | |
122 | domain_id=conf.key_manager.domain_id, | |
123 | domain_name=conf.key_manager.domain_name, | |
124 | project_id=conf.key_manager.project_id, | |
125 | project_name=conf.key_manager.project_name, | |
126 | project_domain_id=conf.key_manager.domain_id, | |
127 | project_domain_name=conf.key_manager.domain_name, | |
128 | reauthenticate=conf.key_manager.reauthenticate) | |
129 | ||
130 | else: | |
131 | LOG.error("Invalid auth_type specified.") | |
132 | raise exception.AuthTypeInvalidError( | |
133 | type=conf.key_manager.auth_type) | |
134 | ||
135 | # for compatibility between _TokenData and RequestContext | |
136 | if hasattr(context, 'tenant') and context.tenant: | |
137 | project_id = context.tenant | |
138 | elif hasattr(context, 'project_id') and context.project_id: | |
139 | project_id = context.project_id | |
140 | ||
141 | return keystone_token.KeystoneToken( | |
142 | context.auth_token, | |
143 | project_id=project_id) |
19 | 19 | from castellan.key_manager import barbican_key_manager as bkm |
20 | 20 | except ImportError: |
21 | 21 | bkm = None |
22 | from castellan.common import utils | |
22 | 23 | |
23 | 24 | _DEFAULT_LOG_LEVELS = ['castellan=WARN'] |
24 | 25 | |
93 | 94 | |
94 | 95 | :returns: a list of (group_name, opts) tuples |
95 | 96 | """ |
96 | opts = [('key_manager', km.key_manager_opts)] | |
97 | key_manager_opts = [] | |
98 | key_manager_opts.extend(km.key_manager_opts) | |
99 | key_manager_opts.extend(utils.credential_opts) | |
100 | opts = [('key_manager', key_manager_opts)] | |
101 | ||
97 | 102 | if bkm is not None: |
98 | 103 | opts.append((bkm.BARBICAN_OPT_GROUP, bkm.barbican_opts)) |
99 | 104 | return opts |
0 | # Copyright (c) 2016 IBM | |
1 | # Licensed under the Apache License, Version 2.0 (the "License"); | |
2 | # you may not use this file except in compliance with the License. | |
3 | # You may obtain a copy of the License at | |
4 | # | |
5 | # http://www.apache.org/licenses/LICENSE-2.0 | |
6 | # | |
7 | # Unless required by applicable law or agreed to in writing, software | |
8 | # distributed under the License is distributed on an "AS IS" BASIS, | |
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
10 | # implied. | |
11 | # See the License for the specific language governing permissions and | |
12 | # limitations under the License. | |
13 | ||
14 | """ | |
15 | Test Common utilities for Castellan. | |
16 | """ | |
17 | ||
18 | from castellan.common import exception | |
19 | from castellan.common import utils | |
20 | from castellan.tests import base | |
21 | ||
22 | from oslo_config import cfg | |
23 | from oslo_config import fixture as config_fixture | |
24 | from oslo_context import context | |
25 | ||
26 | CONF = cfg.CONF | |
27 | ||
28 | ||
29 | class TestUtils(base.TestCase): | |
30 | ||
31 | def setUp(self): | |
32 | super(TestUtils, self).setUp() | |
33 | self.config_fixture = self.useFixture(config_fixture.Config(CONF)) | |
34 | CONF.register_opts(utils.credential_opts, group=utils.OPT_GROUP) | |
35 | ||
36 | def test_token_credential(self): | |
37 | token_value = 'ec9799cd921e4e0a8ab6111c08ebf065' | |
38 | ||
39 | self.config_fixture.config( | |
40 | auth_type='token', | |
41 | token=token_value, | |
42 | group='key_manager' | |
43 | ) | |
44 | ||
45 | token_context = utils.credential_factory(conf=CONF) | |
46 | token_context_class = token_context.__class__.__name__ | |
47 | ||
48 | self.assertEqual('Token', token_context_class) | |
49 | self.assertEqual(token_value, token_context.token) | |
50 | ||
51 | def test_token_credential_with_context(self): | |
52 | token_value = 'ec9799cd921e4e0a8ab6111c08ebf065' | |
53 | ctxt = context.RequestContext(auth_token=token_value) | |
54 | ||
55 | self.config_fixture.config( | |
56 | auth_type='token', | |
57 | group='key_manager' | |
58 | ) | |
59 | ||
60 | token_context = utils.credential_factory(conf=CONF, context=ctxt) | |
61 | token_context_class = token_context.__class__.__name__ | |
62 | ||
63 | self.assertEqual('Token', token_context_class) | |
64 | self.assertEqual(token_value, token_context.token) | |
65 | ||
66 | def test_token_credential_config_override_context(self): | |
67 | ctxt_token_value = '00000000000000000000000000000000' | |
68 | ctxt = context.RequestContext(auth_token=ctxt_token_value) | |
69 | ||
70 | conf_token_value = 'ec9799cd921e4e0a8ab6111c08ebf065' | |
71 | ||
72 | self.config_fixture.config( | |
73 | auth_type='token', | |
74 | token=conf_token_value, | |
75 | group='key_manager' | |
76 | ) | |
77 | ||
78 | token_context = utils.credential_factory(conf=CONF, context=ctxt) | |
79 | token_context_class = token_context.__class__.__name__ | |
80 | ||
81 | self.assertEqual('Token', token_context_class) | |
82 | self.assertEqual(conf_token_value, token_context.token) | |
83 | ||
84 | def test_token_credential_exception(self): | |
85 | self.config_fixture.config( | |
86 | auth_type='token', | |
87 | group='key_manager' | |
88 | ) | |
89 | ||
90 | self.assertRaises(exception.InsufficientCredentialDataError, | |
91 | utils.credential_factory, | |
92 | CONF) | |
93 | ||
94 | def test_password_credential(self): | |
95 | password_value = 'p4ssw0rd' | |
96 | ||
97 | self.config_fixture.config( | |
98 | auth_type='password', | |
99 | password=password_value, | |
100 | group='key_manager' | |
101 | ) | |
102 | ||
103 | password_context = utils.credential_factory(conf=CONF) | |
104 | password_context_class = password_context.__class__.__name__ | |
105 | ||
106 | self.assertEqual('Password', password_context_class) | |
107 | self.assertEqual(password_value, password_context.password) | |
108 | ||
109 | def test_keystone_token_credential(self): | |
110 | token_value = 'ec9799cd921e4e0a8ab6111c08ebf065' | |
111 | ||
112 | self.config_fixture.config( | |
113 | auth_type='keystone_token', | |
114 | token=token_value, | |
115 | group='key_manager' | |
116 | ) | |
117 | ||
118 | ks_token_context = utils.credential_factory(conf=CONF) | |
119 | ks_token_context_class = ks_token_context.__class__.__name__ | |
120 | ||
121 | self.assertEqual('KeystoneToken', ks_token_context_class) | |
122 | self.assertEqual(token_value, ks_token_context.token) | |
123 | ||
124 | def test_keystone_token_credential_with_context(self): | |
125 | token_value = 'ec9799cd921e4e0a8ab6111c08ebf065' | |
126 | ctxt = context.RequestContext(auth_token=token_value) | |
127 | ||
128 | self.config_fixture.config( | |
129 | auth_type='keystone_token', | |
130 | group='key_manager' | |
131 | ) | |
132 | ||
133 | ks_token_context = utils.credential_factory(conf=CONF, context=ctxt) | |
134 | ks_token_context_class = ks_token_context.__class__.__name__ | |
135 | ||
136 | self.assertEqual('KeystoneToken', ks_token_context_class) | |
137 | self.assertEqual(token_value, ks_token_context.token) | |
138 | ||
139 | def test_keystone_token_credential_config_override_context(self): | |
140 | ctxt_token_value = 'ec9799cd921e4e0a8ab6111c08ebf065' | |
141 | ctxt = context.RequestContext(auth_token=ctxt_token_value) | |
142 | ||
143 | conf_token_value = 'ec9799cd921e4e0a8ab6111c08ebf065' | |
144 | ||
145 | self.config_fixture.config( | |
146 | auth_type='keystone_token', | |
147 | token=conf_token_value, | |
148 | group='key_manager' | |
149 | ) | |
150 | ||
151 | ks_token_context = utils.credential_factory(conf=CONF, context=ctxt) | |
152 | ks_token_context_class = ks_token_context.__class__.__name__ | |
153 | ||
154 | self.assertEqual('KeystoneToken', ks_token_context_class) | |
155 | self.assertEqual(conf_token_value, ks_token_context.token) | |
156 | ||
157 | def test_keystone_token_credential_exception(self): | |
158 | self.config_fixture.config( | |
159 | auth_type='keystone_token', | |
160 | group='key_manager' | |
161 | ) | |
162 | ||
163 | self.assertRaises(exception.InsufficientCredentialDataError, | |
164 | utils.credential_factory, | |
165 | CONF) | |
166 | ||
167 | def test_keystone_password_credential(self): | |
168 | password_value = 'p4ssw0rd' | |
169 | ||
170 | self.config_fixture.config( | |
171 | auth_type='keystone_password', | |
172 | password=password_value, | |
173 | group='key_manager' | |
174 | ) | |
175 | ||
176 | ks_password_context = utils.credential_factory(conf=CONF) | |
177 | ks_password_context_class = ks_password_context.__class__.__name__ | |
178 | ||
179 | self.assertEqual('KeystonePassword', ks_password_context_class) | |
180 | self.assertEqual(password_value, ks_password_context.password) | |
181 | ||
182 | def test_oslo_context_to_keystone_token(self): | |
183 | auth_token_value = '16bd612f28ec479b8ffe8e124fc37b43' | |
184 | tenant_value = '00c6ef5ad2984af2acd7d42c299935c0' | |
185 | ||
186 | ctxt = context.RequestContext( | |
187 | auth_token=auth_token_value, | |
188 | tenant=tenant_value) | |
189 | ||
190 | ks_token_context = utils.credential_factory(context=ctxt) | |
191 | ks_token_context_class = ks_token_context.__class__.__name__ | |
192 | ||
193 | self.assertEqual('KeystoneToken', ks_token_context_class) | |
194 | self.assertEqual(auth_token_value, ks_token_context.token) | |
195 | self.assertEqual(tenant_value, ks_token_context.project_id) | |
196 | ||
197 | def test_invalid_auth_type(self): | |
198 | self.config_fixture.config( | |
199 | auth_type='hotdog', | |
200 | group='key_manager' | |
201 | ) | |
202 | ||
203 | self.assertRaises(exception.AuthTypeInvalidError, | |
204 | utils.credential_factory, | |
205 | conf=CONF) |