~ubuntu-branches/ubuntu/trusty/heat/trusty-security

« back to all changes in this revision

Viewing changes to heat/common/heat_keystoneclient.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2013-10-03 09:43:04 UTC
  • mto: This revision was merged to the branch mainline in revision 15.
  • Revision ID: package-import@ubuntu.com-20131003094304-zhhr2brapzlpvjmm
Tags: upstream-2013.2~rc1
ImportĀ upstreamĀ versionĀ 2013.2~rc1

Show diffs side-by-side

added added

removed removed

Lines of Context:
13
13
#    License for the specific language governing permissions and limitations
14
14
#    under the License.
15
15
 
 
16
from heat.common import context
16
17
from heat.common import exception
17
18
 
18
19
import eventlet
19
 
import hashlib
20
20
 
21
21
from keystoneclient.v2_0 import client as kc
22
22
from keystoneclient.v3 import client as kc_v3
38
38
    directly instantiate instances of this class inside resources themselves
39
39
    """
40
40
    def __init__(self, context):
41
 
        self.context = context
42
41
        # We have to maintain two clients authenticated with keystone:
43
42
        # - ec2 interface is v2.0 only
44
43
        # - trusts is v3 only
45
 
        # - passing a v2 auth_token to the v3 client won't work until lp bug
46
 
        #   #1212778 is fixed
47
 
        # - passing a v3 token to the v2 client works but we have to either
48
 
        #   md5sum it or use the nocatalog option to auth/tokens (not yet
49
 
        #   supported by keystoneclient), or we hit the v2 8192byte size limit
 
44
        # If a trust_id is specified in the context, we immediately
 
45
        # authenticate so we can populate the context with a trust token
 
46
        # otherwise, we delay client authentication until needed to avoid
 
47
        # unnecessary calls to keystone.
 
48
        #
 
49
        # Note that when you obtain a token using a trust, it cannot be
 
50
        # used to reauthenticate and get another token, so we have to
 
51
        # get a new trust-token even if context.auth_token is set.
 
52
        #
50
53
        # - context.auth_url is expected to contain the v2.0 keystone endpoint
51
 
        if cfg.CONF.deferred_auth_method == 'trusts':
 
54
        self.context = context
 
55
        self._client_v2 = None
 
56
        self._client_v3 = None
 
57
 
 
58
        if self.context.trust_id:
 
59
            # Create a connection to the v2 API, with the trust_id, this
 
60
            # populates self.context.auth_token with a trust-scoped token
 
61
            self._client_v2 = self._v2_client_init()
 
62
 
 
63
    @property
 
64
    def client_v3(self):
 
65
        if not self._client_v3:
52
66
            # Create connection to v3 API
53
 
            self.client_v3 = self._v3_client_init()
54
 
 
55
 
            # Set context auth_token to md5sum of v3 token
56
 
            auth_token = self.client_v3.auth_ref.get('auth_token')
57
 
            self.context.auth_token = self._md5_token(auth_token)
58
 
 
59
 
            # Create the connection to the v2 API, reusing the md5-ified token
60
 
            self.client_v2 = self._v2_client_init()
61
 
        else:
62
 
            # Create the connection to the v2 API, using the context creds
63
 
            self.client_v2 = self._v2_client_init()
64
 
            self.client_v3 = None
65
 
 
66
 
    def _md5_token(self, auth_token):
67
 
        # Get the md5sum of the v3 token, which we can pass instead of the
68
 
        # actual token to avoid v2 8192byte size limit on the v2 token API
69
 
        m_enc = hashlib.md5()
70
 
        m_enc.update(auth_token)
71
 
        return m_enc.hexdigest()
 
67
            self._client_v3 = self._v3_client_init()
 
68
        return self._client_v3
 
69
 
 
70
    @property
 
71
    def client_v2(self):
 
72
        if not self._client_v2:
 
73
            self._client_v2 = self._v2_client_init()
 
74
        return self._client_v2
72
75
 
73
76
    def _v2_client_init(self):
74
77
        kwargs = {
75
78
            'auth_url': self.context.auth_url
76
79
        }
77
 
        # Note check for auth_token first so we use existing token if
78
 
        # available from v3 auth
79
 
        if self.context.auth_token is not None:
 
80
        auth_kwargs = {}
 
81
        # Note try trust_id first, as we can't reuse auth_token in that case
 
82
        if self.context.trust_id is not None:
 
83
            # We got a trust_id, so we use the admin credentials
 
84
            # to authenticate, then re-scope the token to the
 
85
            # trust impersonating the trustor user.
 
86
            # Note that this currently requires the trustor tenant_id
 
87
            # to be passed to the authenticate(), unlike the v3 call
 
88
            kwargs.update(self._service_admin_creds(api_version=2))
 
89
            auth_kwargs['trust_id'] = self.context.trust_id
 
90
            auth_kwargs['tenant_id'] = self.context.tenant_id
 
91
        elif self.context.auth_token is not None:
80
92
            kwargs['tenant_name'] = self.context.tenant
81
93
            kwargs['token'] = self.context.auth_token
82
94
        elif self.context.password is not None:
89
101
                         "auth_token!")
90
102
            raise exception.AuthorizationFailure()
91
103
        client_v2 = kc.Client(**kwargs)
92
 
        if not client_v2.authenticate():
93
 
            logger.error("Keystone v2 API authentication failed")
94
 
            raise exception.AuthorizationFailure()
 
104
 
 
105
        client_v2.authenticate(**auth_kwargs)
 
106
        # If we are authenticating with a trust auth_kwargs are set, so set
 
107
        # the context auth_token with the re-scoped trust token
 
108
        if auth_kwargs:
 
109
            # Sanity check
 
110
            if not client_v2.auth_ref.trust_scoped:
 
111
                logger.error("v2 trust token re-scoping failed!")
 
112
                raise exception.AuthorizationFailure()
 
113
            # All OK so update the context with the token
 
114
            self.context.auth_token = client_v2.auth_ref.auth_token
 
115
            self.context.auth_url = kwargs.get('auth_url')
 
116
 
95
117
        return client_v2
96
118
 
97
119
    @staticmethod
139
161
                         "auth_token!")
140
162
            raise exception.AuthorizationFailure()
141
163
 
142
 
        client_v3 = kc_v3.Client(**kwargs)
143
 
        if not client_v3.authenticate():
144
 
            logger.error("Keystone v3 API authentication failed")
145
 
            raise exception.AuthorizationFailure()
146
 
        return client_v3
 
164
        client = kc_v3.Client(**kwargs)
 
165
        # Have to explicitly authenticate() or client.auth_ref is None
 
166
        client.authenticate()
 
167
 
 
168
        return client
147
169
 
148
170
    def create_trust_context(self):
149
171
        """
150
172
        If cfg.CONF.deferred_auth_method is trusts, we create a
151
173
        trust using the trustor identity in the current context, with the
152
 
        trustee as the heat service user
153
 
 
154
 
        If deferred_auth_method != trusts, we do nothing
155
 
 
156
 
        If the current context already contains a trust_id, we do nothing
 
174
        trustee as the heat service user and return a context containing
 
175
        the new trust_id
 
176
 
 
177
        If deferred_auth_method != trusts, or the current context already
 
178
        contains a trust_id, we do nothing and return the current context
157
179
        """
158
 
        if cfg.CONF.deferred_auth_method != 'trusts':
159
 
            return
160
 
 
161
180
        if self.context.trust_id:
162
 
            return
 
181
            return self.context
163
182
 
164
183
        # We need the service admin user ID (not name), as the trustor user
165
184
        # can't lookup the ID in keystoneclient unless they're admin
167
186
        # then getting the user ID from the auth_ref
168
187
        admin_creds = self._service_admin_creds()
169
188
        admin_client = kc.Client(**admin_creds)
170
 
        if not admin_client.authenticate():
171
 
            logger.error("Keystone v2 API admin authentication failed")
172
 
            raise exception.AuthorizationFailure()
173
 
 
174
 
        trustee_user_id = admin_client.auth_ref['user']['id']
175
 
        trustor_user_id = self.client_v3.auth_ref['user']['id']
176
 
        trustor_project_id = self.client_v3.auth_ref['project']['id']
 
189
        trustee_user_id = admin_client.auth_ref.user_id
 
190
        trustor_user_id = self.client_v3.auth_ref.user_id
 
191
        trustor_project_id = self.client_v3.auth_ref.project_id
177
192
        roles = cfg.CONF.trusts_delegated_roles
178
193
        trust = self.client_v3.trusts.create(trustor_user=trustor_user_id,
179
194
                                             trustee_user=trustee_user_id,
180
195
                                             project=trustor_project_id,
181
196
                                             impersonation=True,
182
197
                                             role_names=roles)
183
 
        self.context.trust_id = trust.id
184
 
        self.context.trustor_user_id = trustor_user_id
185
 
 
186
 
    def delete_trust_context(self):
187
 
        """
188
 
        If a trust_id exists in the context, we delete it
189
 
 
190
 
        """
191
 
        if not self.context.trust_id:
192
 
            return
193
 
 
194
 
        self.client_v3.trusts.delete(self.context.trust_id)
195
 
 
196
 
        self.context.trust_id = None
197
 
        self.context.trustor_user_id = None
 
198
 
 
199
        trust_context = context.RequestContext.from_dict(
 
200
            self.context.to_dict())
 
201
        trust_context.trust_id = trust.id
 
202
        trust_context.trustor_user_id = trustor_user_id
 
203
        return trust_context
 
204
 
 
205
    def delete_trust(self, trust_id):
 
206
        """
 
207
        Delete the specified trust.
 
208
        """
 
209
        self.client_v3.trusts.delete(trust_id)
198
210
 
199
211
    def create_stack_user(self, username, password=''):
200
212
        """