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 6 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
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ gem 'azure_mgmt_subscriptions', path: 'management/azure_mgmt_subscri
gem 'azure_mgmt_traffic_manager', path: 'management/azure_mgmt_traffic_manager'
gem 'azure_mgmt_web', path: 'management/azure_mgmt_web'
gem 'azure_sdk', path: 'azure_sdk'
gem 'azure-core', git: 'https://github.com/katmsft/azure-ruby-asm-core.git', branch: 'master'
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert use of private fork after release of azure-core version 0.1.11

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
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