From 6bf14a46a5f63dcc130f8cae85462dd5246b2156 Mon Sep 17 00:00:00 2001 From: Veronica Giaudrone Date: Mon, 18 Dec 2017 15:46:34 -0800 Subject: [PATCH] changes to ms_rest_azure to enable user assigned identity --- runtime/ms_rest_azure/CHANGELOG.md | 3 ++ .../credentials/msi_token_provider.rb | 48 +++++++++++++++++-- .../lib/ms_rest_azure/version.rb | 2 +- .../spec/msi_token_provider_spec.rb | 23 +++++++++ 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/runtime/ms_rest_azure/CHANGELOG.md b/runtime/ms_rest_azure/CHANGELOG.md index 651fbfdbf..929cdeb52 100644 --- a/runtime/ms_rest_azure/CHANGELOG.md +++ b/runtime/ms_rest_azure/CHANGELOG.md @@ -1,3 +1,6 @@ +##2017.12.19 ms_rest_azure version 0.10.1 +* Added support for user assigned identity to MSITokenProvider Modified portal URLs for Azure cloud environments. Refer [Issue #1175](https://github.com/Azure/azure-sdk-for-ruby/issues/1175) for further details. + ##2017.11.10 ms_rest_azure version 0.10.0 * Modified portal URLs for Azure cloud environments. Refer [PR #1106](https://github.com/Azure/azure-sdk-for-ruby/pull/1106) for further details. * [Breaking Change] Removed Resource and SubResource classes. Refer [PR #1106](https://github.com/Azure/azure-sdk-for-ruby/pull/1106) for further details. diff --git a/runtime/ms_rest_azure/lib/ms_rest_azure/credentials/msi_token_provider.rb b/runtime/ms_rest_azure/lib/ms_rest_azure/credentials/msi_token_provider.rb index ff4a178c5..d284d57d2 100644 --- a/runtime/ms_rest_azure/lib/ms_rest_azure/credentials/msi_token_provider.rb +++ b/runtime/ms_rest_azure/lib/ms_rest_azure/credentials/msi_token_provider.rb @@ -12,6 +12,7 @@ module MsRestAzure TOKEN_ACQUIRE_URL = 'http://localhost:{port}/oauth2/token' REQUEST_BODY_PATTERN = 'resource={resource_uri}' + USER_ASSIGNED_IDENTITY = '{id_type}={user_assigned_identity}' DEFAULT_SCHEME = 'Bearer' # @return [MSIActiveDirectoryServiceSettings] settings. @@ -20,6 +21,15 @@ module MsRestAzure # @return [Integer] port number where MSI service is running. attr_accessor :port + # @return [String] client id for user assigned managed identity + attr_accessor :client_id + + # @return [String] object id for user assigned managed identity + attr_accessor :object_id + + # @return [String] ms_res id for user assigned managed identity + attr_accessor :msi_res_id + # @return [String] auth token. attr_accessor :token @@ -38,13 +48,26 @@ module MsRestAzure # Creates and initialize new instance of the MSITokenProvider class. # @param port [Integer] port number where MSI service is running. # @param settings [ActiveDirectoryServiceSettings] active directory setting. - def initialize(port = 50342, settings = ActiveDirectoryServiceSettings.get_azure_settings) + # @param msi_id [Hash] MSI id for user assigned managed service identity, + # msi_id = {'client_id': 'client id of user assigned identity'} + # or + # msi_id = {'object_id': 'object id of user assigned identity'} + # or + # msi_id = {'msi_rest_id': 'resource id of user assigned identity'} + # The above key,value pairs are mutually exclusive. + def initialize(port = 50342, settings = ActiveDirectoryServiceSettings.get_azure_settings, msi_id = nil) fail ArgumentError, 'Port cannot be nil' if port.nil? fail ArgumentError, 'Port must be an Integer' unless port.is_a? Integer fail ArgumentError, 'Azure AD settings cannot be nil' if settings.nil? + fail ArgumentError, 'msi_id must include either client_id, object_id or msi_res_id exclusively' if (!msi_id.nil? && msi_id.length > 1) @port = port @settings = settings + if !msi_id.nil? + @client_id = msi_id[:client_id] unless msi_id[:client_id].nil? + @object_id = msi_id[:object_id] unless msi_id[:object_id].nil? + @msi_res_id = msi_id[:msi_res_id] unless msi_id[:msi_res_id].nil? + end @expiration_threshold = 5 * 60 end @@ -85,6 +108,9 @@ module MsRestAzure request_body = REQUEST_BODY_PATTERN.dup request_body['{resource_uri}'] = ERB::Util.url_encode(@settings.token_audience) + request_body = set_msi_id(request_body, 'client_id', @client_id) unless @client_id.nil? + request_body = set_msi_id(request_body, 'object_id', @object_id) unless @object_id.nil? + request_body = set_msi_id(request_body, 'msi_res_id', @msi_res_id) unless @msi_res_id.nil? response = connection.post do |request| request.headers['content-type'] = 'application/x-www-form-urlencoded' @@ -93,13 +119,29 @@ module MsRestAzure end fail AzureOperationError, - 'Couldn\'t acquire access token from Managed Service Identity, please verify your tenant id, port and settings' unless response.status == 200 + 'Couldn\'t acquire access token from Managed Service Identity, please verify your tenant id, port and settings' unless response.status == 200 response_body = JSON.load(response.body) @token = response_body['access_token'] @token_expires_on = Time.at(Integer(response_body['expires_on'])) @token_type = response_body['token_type'] end + + # + # Sets user assigned identity value in request body + # @param request_body [String] body of the request used to acquire token + # @param id_type [String] type of id to send 'client_id', 'object_id' or 'msi_res_id' + # @param id_value [String] id of the user assigned identity + # + # @return [String] new authentication token. + def set_msi_id(request_body, id_type, id_value ) + user_assigned_identity = USER_ASSIGNED_IDENTITY.dup + request_body = [request_body, user_assigned_identity].join(', ') + request_body['{id_type}'] = id_type + request_body['{user_assigned_identity}'] = ERB::Util.url_encode(id_value) + + return request_body + end end -end +end \ No newline at end of file diff --git a/runtime/ms_rest_azure/lib/ms_rest_azure/version.rb b/runtime/ms_rest_azure/lib/ms_rest_azure/version.rb index e58278bf0..0ba504649 100644 --- a/runtime/ms_rest_azure/lib/ms_rest_azure/version.rb +++ b/runtime/ms_rest_azure/lib/ms_rest_azure/version.rb @@ -3,5 +3,5 @@ # Licensed under the MIT License. See License.txt in the project root for license information. module MsRestAzure - VERSION = '0.10.0' + VERSION = '0.10.1' end diff --git a/runtime/ms_rest_azure/spec/msi_token_provider_spec.rb b/runtime/ms_rest_azure/spec/msi_token_provider_spec.rb index ebfcc0dc1..adc21c5bc 100644 --- a/runtime/ms_rest_azure/spec/msi_token_provider_spec.rb +++ b/runtime/ms_rest_azure/spec/msi_token_provider_spec.rb @@ -35,6 +35,29 @@ module MsRestAzure expect(settings.authentication_endpoint).to eq(settings.authentication_endpoint) expect(settings.token_audience).to eq(settings.token_audience) end + + it 'should throw error if more than one value is passed to msi_id' do + expect { MSITokenProvider.new(50342, ActiveDirectoryServiceSettings.get_azure_settings, {'client_id': '1234', 'object_id': '5678'}) }.to raise_error(ArgumentError) + end + + it 'should set msi_id for user assigned identity - using client_id' do + id = '1234' + token_provider = MSITokenProvider.new(50342, ActiveDirectoryServiceSettings.get_azure_settings, {'client_id': id}) + expect(token_provider.send('client_id')).to eq(id) + end + + it 'should set msi_id for user assigned identity - using object_id' do + id = '1234' + token_provider = MSITokenProvider.new(50342, ActiveDirectoryServiceSettings.get_azure_settings, {'object_id': id}) + expect(token_provider.send('object_id')).to eq(id) + end + + it 'should set msi_id for user assigned identity - using msi_res_id' do + id = '1234' + token_provider = MSITokenProvider.new(50342, ActiveDirectoryServiceSettings.get_azure_settings, {'msi_res_id': id}) + expect(token_provider.send('msi_res_id')).to eq(id) + end + end end