# frozen_string_literal: truemoduleAws# An auto-refreshing credential provider that assumes a role via# {Aws::SSO::Client#get_role_credentials} using a cached access# token. When `sso_session` is specified, token refresh logic from# {Aws::SSOTokenProvider} will be used to refresh the token if possible.# This class does NOT implement the SSO login token flow - tokens# must generated separately by running `aws login` from the# AWS CLI with the correct profile. The `SSOCredentials` will# auto-refresh the AWS credentials from SSO.## # You must first run aws sso login --profile your-sso-profile# sso_credentials = Aws::SSOCredentials.new(# sso_account_id: '123456789',# sso_role_name: "role_name",# sso_region: "us-east-1",# sso_session: 'my_sso_session'# )# ec2 = Aws::EC2::Client.new(credentials: sso_credentials)## If you omit `:client` option, a new {Aws::SSO::Client} object will be# constructed with additional options that were provided.## @see Aws::SSO::Client#get_role_credentials# @see https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.htmlclassSSOCredentialsincludeCredentialProviderincludeRefreshingCredentials# @api privateLEGACY_REQUIRED_OPTS=[:sso_start_url,:sso_account_id,:sso_region,:sso_role_name].freezeTOKEN_PROVIDER_REQUIRED_OPTS=[:sso_session,:sso_account_id,:sso_region,:sso_role_name].freeze# @api privateSSO_LOGIN_GUIDANCE='The SSO session associated with this profile has '\'expired or is otherwise invalid. To refresh this SSO session run '\'aws sso login with the corresponding profile.'.freeze# @option options [required, String] :sso_account_id The AWS account ID# that temporary AWS credentials will be resolved for## @option options [required, String] :sso_role_name The corresponding# IAM role in the AWS account that temporary AWS credentials# will be resolved for.## @option options [required, String] :sso_region The AWS region where the# SSO directory for the given sso_start_url is hosted.## @option options [String] :sso_session The SSO Token used for fetching# the token. If provided, refresh logic from the {Aws::SSOTokenProvider}# will be used.## @option options [String] :sso_start_url (legacy profiles) If provided,# legacy token fetch behavior will be used, which does not support# token refreshing. The start URL is provided by the SSO# service via the console and is the URL used to# login to the SSO directory. This is also sometimes referred to as# the "User Portal URL".## @option options [SSO::Client] :client Optional `SSO::Client`. If not# provided, a client will be constructed.## @option options [Callable] before_refresh Proc called before# credentials are refreshed. `before_refresh` is called# with an instance of this object when# AWS credentials are required and need to be refreshed.definitialize(options={})options=options.select{|k,v|!v.nil?}if(options[:sso_session])missing_keys=TOKEN_PROVIDER_REQUIRED_OPTS.select{|k|options[k].nil?}unlessmissing_keys.empty?raiseArgumentError,"Missing required keys: #{missing_keys}"end@legacy=false@sso_role_name=options.delete(:sso_role_name)@sso_account_id=options.delete(:sso_account_id)# if client has been passed, don't pass through to SSOTokenProvider@client=options.delete(:client)options.delete(:sso_start_url)@token_provider=Aws::SSOTokenProvider.new(options.dup)@sso_session=options.delete(:sso_session)@sso_region=options.delete(:sso_region)unless@clientclient_opts={}options.each_pair{|k,v|client_opts[k]=vunlessCLIENT_EXCLUDE_OPTIONS.include?(k)}client_opts[:region]=@sso_regionclient_opts[:credentials]=nil@client=Aws::SSO::Client.new(client_opts)endelse# legacy behaviormissing_keys=LEGACY_REQUIRED_OPTS.select{|k|options[k].nil?}unlessmissing_keys.empty?raiseArgumentError,"Missing required keys: #{missing_keys}"end@legacy=true@sso_start_url=options.delete(:sso_start_url)@sso_region=options.delete(:sso_region)@sso_role_name=options.delete(:sso_role_name)@sso_account_id=options.delete(:sso_account_id)# validate we can read the token fileread_cached_tokenclient_opts={}options.each_pair{|k,v|client_opts[k]=vunlessCLIENT_EXCLUDE_OPTIONS.include?(k)}client_opts[:region]=@sso_regionclient_opts[:credentials]=nil@client=options[:client]||Aws::SSO::Client.new(client_opts)end@async_refresh=truesuperend# @return [SSO::Client]attr_reader:clientprivatedefread_cached_tokencached_token=Json.load(File.read(sso_cache_file))# validationunlesscached_token['accessToken']&&cached_token['expiresAt']raiseArgumentError,'Missing required field(s)'endexpires_at=DateTime.parse(cached_token['expiresAt'])ifexpires_at<DateTime.nowraiseArgumentError,'Cached SSO Token is expired.'endcached_tokenrescueErrno::ENOENT,Aws::Json::ParseError,ArgumentErrorraiseErrors::InvalidSSOCredentials,SSO_LOGIN_GUIDANCEenddefrefreshc=if@legacycached_token=read_cached_token@client.get_role_credentials(account_id: @sso_account_id,role_name: @sso_role_name,access_token: cached_token['accessToken']).role_credentialselse@client.get_role_credentials(account_id: @sso_account_id,role_name: @sso_role_name,access_token: @token_provider.token.token).role_credentialsend@credentials=Credentials.new(c.access_key_id,c.secret_access_key,c.session_token)@expiration=Time.at(c.expiration/1000.0)enddefsso_cache_filestart_url_sha1=OpenSSL::Digest::SHA1.hexdigest(@sso_start_url.encode('utf-8'))File.join(Dir.home,'.aws','sso','cache',"#{start_url_sha1}.json")rescueArgumentError# Dir.home raises ArgumentError when ENV['home'] is not setraiseArgumentError,"Unable to load sso_cache_file: ENV['HOME'] is not set."endendend