Skip to content
This repository was archived by the owner on Jan 11, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ matrix:
- rvm: 2.3.0
env: LATEST_RUNTIME=true
before_install:
- gem install bundler -v 1.14.1
- if [ "$LATEST_RUNTIME" == "true" ] ; then ./scripts/latest_runtime.sh ; fi
script:
- gem install bundler
- if [ "$INTEG_RECORDED" == "true" ] ; then bundle install --gemfile=Gemfile && bundle exec rake arm:spec ; fi
- bundle exec rake arm:build
- unset BUNDLE_GEMFILE
Expand Down
2 changes: 1 addition & 1 deletion azure_sdk/azure_sdk.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,5 @@ Gem::Specification.new do |spec|
spec.add_runtime_dependency 'azure_mgmt_subscriptions', "~>#{version}"
spec.add_runtime_dependency 'azure_mgmt_traffic_manager', "~>#{version}"
spec.add_runtime_dependency 'azure_mgmt_web', "~>#{version}"
spec.add_runtime_dependency 'azure-storage', "~>0.12.1.preview"
spec.add_runtime_dependency 'azure-storage', "~>0.12.3.preview"
end
3 changes: 3 additions & 0 deletions runtime/ms_rest_azure/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
##Unreleased ms_rest_azure version 0.8.x
* Enable Managed Service Identity authentication features into ms-rest-azure runtime for azure_mgmt_* sdks.[Issue #884](https://github.com/Azure/azure-sdk-for-ruby/issues/884) [PR #889](https://github.com/Azure/azure-sdk-for-ruby/pull/889)

##2017.07.10 ms_rest_azure version 0.8.1
* [Bug Fix] Fixed the issue with the polling status object to handle the response code and provisioning status correctly.[Issue #817](https://github.com/Azure/azure-sdk-for-ruby/issues/817) [PR #828](https://github.com/Azure/azure-sdk-for-ruby/pull/828)

Expand Down
1 change: 1 addition & 0 deletions runtime/ms_rest_azure/lib/ms_rest_azure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
require 'ms_rest_azure/azure_service_client.rb'
require 'ms_rest_azure/cloud_error_data.rb'
require 'ms_rest_azure/credentials/application_token_provider.rb'
require 'ms_rest_azure/credentials/msi_token_provider.rb'
require 'ms_rest_azure/polling_state.rb'
require 'ms_rest_azure/sub_resource.rb'
require 'ms_rest_azure/resource.rb'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ApplicationTokenProvider < MsRest::TokenProvider
# @param tenant_id [String] tenant id (also known as domain).
# @param client_id [String] client id.
# @param client_secret [String] client secret.
# @param settings [ActiveDirectoryServiceSettings] client secret.
# @param settings [ActiveDirectoryServiceSettings] active directory setting.
def initialize(tenant_id, client_id, client_secret, settings = ActiveDirectoryServiceSettings.get_azure_settings)
fail ArgumentError, 'Tenant id cannot be nil' if tenant_id.nil?
fail ArgumentError, 'Client id cannot be nil' if client_id.nil?
Expand Down Expand Up @@ -81,7 +81,7 @@ def token_expired
end

#
# Retrieves a new authenticaion token.
# Retrieves a new authentication token.
#
# @return [String] new authentication token.
def acquire_token
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# encoding: utf-8
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.

module MsRestAzure
#
# Class that provides access to authentication token via Managed Service Identity.
#
class MSITokenProvider < MsRest::TokenProvider

private

TOKEN_ACQUIRE_URL = 'http://localhost:{port}/oauth2/token'
REQUEST_BODY_PATTERN = 'authority={authentication_endpoint}{tenant_id}&resource={resource_uri}'
DEFAULT_SCHEME = 'Bearer'

# @return [MSIActiveDirectoryServiceSettings] settings.
attr_accessor :settings

# @return [String] tenant id (also known as domain).
attr_accessor :tenant_id

# @return [Integer] port number where MSI service is running.
attr_accessor :port

# @return [String] auth token.
attr_accessor :token

# @return [Time] the date when the current token expires.
attr_accessor :token_expires_on

# @return [Integer] the amount of time we refresh token before it expires.
attr_reader :expiration_threshold

# @return [String] the type of token.
attr_reader :token_type

public

#
# Creates and initialize new instance of the MSITokenProvider class.
# @param tenant_id [String] tenant id (also known as domain).
# @param port [Integer] port number where MSI service is running.
# @param settings [ActiveDirectoryServiceSettings] active directory setting.
def initialize(tenant_id, port = 50342, settings = ActiveDirectoryServiceSettings.get_azure_settings)
fail ArgumentError, 'Tenant id cannot be nil' if tenant_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?

@tenant_id = tenant_id
@port = port
@settings = settings

@expiration_threshold = 5 * 60
end

#
# Returns the string value which needs to be attached
# to HTTP request header in order to be authorized.
#
# @return [String] authentication headers.
def get_authentication_header
acquire_token if token_expired
"#{token_type} #{token}"
end

private

#
# Checks whether token is about to expire.
#
# @return [Bool] True if token is about to expire, false otherwise.
def token_expired
@token.nil? || Time.now >= @token_expires_on + expiration_threshold
end

#
# Retrieves a new authentication token.
#
# @return [String] new authentication token.
def acquire_token
token_acquire_url = TOKEN_ACQUIRE_URL.dup
token_acquire_url['{port}'] = @port.to_s

url = URI.parse(token_acquire_url)

connection = Faraday.new(:url => url, :ssl => MsRest.ssl_options) do |builder|
builder.adapter Faraday.default_adapter
end

request_body = REQUEST_BODY_PATTERN.dup
request_body['{authentication_endpoint}'] = ERB::Util.url_encode(@settings.authentication_endpoint)
request_body['{tenant_id}'] = ERB::Util.url_encode(@tenant_id)
request_body['{resource_uri}'] = ERB::Util.url_encode(@settings.token_audience)

response = connection.post do |request|
request.headers['content-type'] = 'application/x-www-form-urlencoded'
request.body = request_body
end

fail AzureOperationError,
'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
end

end
46 changes: 46 additions & 0 deletions runtime/ms_rest_azure/spec/msi_token_provider_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# encoding: utf-8
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.

require 'rspec'
require 'ms_rest_azure'

module MsRestAzure

describe MSITokenProvider do
it 'should throw error if nil data is passed into constructor' do
expect { MSITokenProvider.new(nil) }.to raise_error(ArgumentError)
expect { MSITokenProvider.new('tenant_id',nil) }.to raise_error(ArgumentError)
expect { MSITokenProvider.new('tenant_id','port') }.to raise_error(ArgumentError)
expect { MSITokenProvider.new('tenant_id',50431,nil) }.to raise_error(ArgumentError)
end

it 'should set defaults for managed service identity' do
tenant = 'xxxx-xxxx-xxxxx-xxxxx'
azure_cloud = MsRestAzure::AzureEnvironments::AzureCloud

token_provider = MSITokenProvider.new(tenant)
expect(token_provider.send(:tenant_id)).to eq(tenant)
expect(token_provider.send(:port)).to eq(50342)
settings = token_provider.send(:settings)
expect(settings.authentication_endpoint).to eq(azure_cloud.active_directory_endpoint_url)
expect(settings.token_audience).to eq(azure_cloud.active_directory_resource_id)
end

it 'should set customs for managed service identity' do
tenant = 'xxxx-xxxx-xxxxx-xxxxx'
port = 50333
settings = ActiveDirectoryServiceSettings.new()
settings.authentication_endpoint = 'https://login.microsoftonline.com/'
settings.token_audience = 'https://vault.azure.net'

token_provider = MSITokenProvider.new(tenant, port, settings)
expect(token_provider.send(:tenant_id)).to eq(tenant)
expect(token_provider.send(:port)).to eq(port)
settings = token_provider.send(:settings)
expect(settings.authentication_endpoint).to eq(settings.authentication_endpoint)
expect(settings.token_audience).to eq(settings.token_audience)
end
end

end