Class: Kontena::Cli::Cloud::LoginCommand

Inherits:
Kontena::Command show all
Includes:
Kontena::Cli::Common
Defined in:
lib/kontena/cli/cloud/login_command.rb

Instance Attribute Summary

Attributes inherited from Kontena::Command

#arguments, #exit_code, #result

Instance Method Summary collapse

Methods included from Kontena::Cli::Common

#access_token=, #add_master, #any_key_to_continue, #any_key_to_continue_with_timeout, #api_url, #api_url=, #api_url_version, #ask, #clear_current_grid, #client, #cloud_auth?, #cloud_client, #config, #confirm, #confirm_command, #current_account, #current_grid, #current_grid=, #current_master, #current_master=, #current_master_index, #display_account_login_info, #display_login_info, #display_logo, #display_master_login_info, #error, #exit_with_error, #kontena_account, #logger, #pastel, #print, #prompt, #puts, #require_api_url, #require_current_account, #require_current_grid, #require_current_master, #require_token, #reset_client, #reset_cloud_client, #running_silent?, #running_verbose?, #settings, #settings_filename, #spinner, #sprint, #sputs, #use_refresh_token, #vfakespinner, #vputs, #vspinner, #warning, #yes?

Methods inherited from Kontena::Command

banner, callback_matcher, #help_requested?, inherited, requires_current_account_token, requires_current_account_token?, requires_current_grid, requires_current_grid?, requires_current_master, requires_current_master?, requires_current_master_token, requires_current_master_token?, #run, #run_callbacks, #verify_current_account_token, #verify_current_grid, #verify_current_master, #verify_current_master_token

Instance Method Details

#executeObject



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/kontena/cli/cloud/login_command.rb', line 12

def execute
  if self.code && self.force?
    exit_with_error "Can't use --code and --force together"
  end

  if self.token
    exit_with_error "Can't use --token and --force together" if self.force?
    exit_with_error "Can't use --token and --code together"  if self.code
  end

  if !.token || !.token.access_token || self.token || self.force?
    .token = Kontena::Cli::Config::Token.new(access_token: self.token, parent_type: :account, parent_name: .name)
  end

  use_authorization_code(self.code) if self.code

  client = Kontena::Client.new(.userinfo_endpoint, .token, prefix: '')

  if .token.access_token
    auth_ok = vspinner "Verifying current access token" do
      client.authentication_ok?(.userinfo_endpoint)
    end
    if auth_ok
      finish and return
    end
  end

  web_flow
  finish
end

#finishObject



43
44
45
46
47
48
49
50
51
# File 'lib/kontena/cli/cloud/login_command.rb', line 43

def finish
  update_userinfo unless .username
  config. = .name
  config.write
  config.reset_instance
  reset_cloud_client
  
  (only: :account)
end

#update_token(response) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/kontena/cli/cloud/login_command.rb', line 119

def update_token(response)
  if !response.kind_of?(Hash)
    raise TypeError, "Invalid authentication response, expected Hash, got #{response.class}"
  elsif response['error']
    exit_with_error "Authentication failed: #{response['error']}"
  elsif response['code']
    use_authorization_code(response['code'])
  else
    .token.access_token  = response['access_token']
    .token.refresh_token = response['refresh_token']
    .token.expires_at    = response['expires_at']
    true
  end
end

#update_userinfoObject



97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/kontena/cli/cloud/login_command.rb', line 97

def update_userinfo
  uri = URI.parse(.userinfo_endpoint)
  path = uri.path
  uri.path = '/'

  response = Kontena::Client.new(uri.to_s, .token).get(path)
  if response.kind_of?(Hash) && response['data'] && response['data']['attributes']
    .username = response['data']['attributes']['username']
  elsif response && response['error']
    exit_with_error response['error']
  else
    exit_with_error "Userinfo request failed"
  end
end

#use_authorization_code(code) ⇒ Object



112
113
114
115
116
117
# File 'lib/kontena/cli/cloud/login_command.rb', line 112

def use_authorization_code(code)
  response = vspinner "Exchanging authorization code to access token" do
    Kontena::Client.new(.token_endpoint, .token).exchange_code(code)
  end
  update_token(response)
end

#web_flowObject



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/kontena/cli/cloud/login_command.rb', line 53

def web_flow
  require_relative '../localhost_web_server'
  require 'launchy'

  uri = URI.parse(.authorization_endpoint)
  uri.host ||= .url

  web_server = Kontena::LocalhostWebServer.new

  params = {
    client_id: .client_id || Kontena::Client::CLIENT_ID,
    response_type: 'code',
    redirect_uri: "http://localhost:#{web_server.port}/cb"
  }

  uri.query = URI.encode_www_form(params)

  puts "Opening a browser to #{uri.scheme}://#{uri.host}"
  #puts
  #puts "If you are running this command over an ssh connection or it's"
  #puts "otherwise not possible to open a browser from this terminal"
  #puts "then you must use a pregenerated access token using the --token"
  #puts "option : kontena cloud login --token <access_token>"
  puts
  puts "Once the authentication is complete you can close the browser"
  puts "window or tab and return to this window to continue."
  puts
  any_key_to_continue(10)

  puts "If the browser does not open, try visiting this URL manually:"
  puts "#{uri.to_s}"
  puts

  server_thread  = Thread.new { Thread.main['response'] = web_server.serve_one }
  browser_thread = Thread.new { Launchy.open(uri.to_s) }
  
  spinner "Waiting for browser authorization response" do
    server_thread.join
  end
  browser_thread.join

  update_token(Thread.main['response'])
end