Class: Aptible::CLI::Helpers::SecurityKey::Authenticator

Inherits:
Object
  • Object
show all
Defined in:
lib/aptible/cli/helpers/security_key.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(auth, pid, out_read, err_read) ⇒ Authenticator

Returns a new instance of Authenticator.



112
113
114
115
116
117
# File 'lib/aptible/cli/helpers/security_key.rb', line 112

def initialize(auth, pid, out_read, err_read)
  @auth = auth
  @pid = pid
  @out_read = out_read
  @err_read = err_read
end

Instance Attribute Details

#authObject (readonly)

Returns the value of attribute auth.



110
111
112
# File 'lib/aptible/cli/helpers/security_key.rb', line 110

def auth
  @auth
end

#pidObject (readonly)

Returns the value of attribute pid.



110
111
112
# File 'lib/aptible/cli/helpers/security_key.rb', line 110

def pid
  @pid
end

Class Method Details

.spawn(auth) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/aptible/cli/helpers/security_key.rb', line 175

def self.spawn(auth)
  in_read, in_write = IO.pipe
  out_read, out_write = IO.pipe
  err_read, err_write = IO.pipe

  pid = Process.spawn(
    "fido2-assert -G #{auth.device_location}",
    in: in_read, out: out_write, err: err_write,
    close_others: true
  )

  U2F_LOGGER.debug("#{self} #{auth.key_handle}: spawned #{pid}")

  [in_read, out_write, err_write].each(&:close)

  in_write.write(auth.assert_str)
  in_write.close

  new(auth, pid, out_read, err_read)
end

Instance Method Details

#exited(status) ⇒ Object



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/aptible/cli/helpers/security_key.rb', line 159

def exited(status)
  out, err = [@out_read, @err_read].map(&:read).map(&:chomp)

  if status.exitstatus == 0
    U2F_LOGGER.info("#{self.class} #{@auth.key_handle}: ok: #{out}")
    [nil, out]
  else
    err_msg = fido_err_msg(err)
    CLI.logger.error(err_msg) if err_msg
    U2F_LOGGER.warn("#{self.class} #{@auth.key_handle}: err: #{err}")
    [ThrottledAuthenticator.spawn(@auth), nil]
  end
ensure
  [@out_read, @err_read].each(&:close)
end

#fido_err_msg(err) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/aptible/cli/helpers/security_key.rb', line 143

def fido_err_msg(err)
  match = err.match(/(FIDO_ERR.+)/)
  return nil unless match
  result = match.captures || []
  no_cred = "\nCredential not found on device, " \
            'are you sure you selected the right ' \
            'credential for this device?'
  err_map = {
    'FIDO_ERR_NO_CREDENTIALS' => no_cred
  }

  return err_map[result[0]] if result.count > 0

  nil
end

#formatted_out(out) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/aptible/cli/helpers/security_key.rb', line 119

def formatted_out(out)
  arr = out.split("\n")
  authenticator_data = arr[2]
  signature = arr[3]
  appid = auth.app_id if auth.version == 'U2F_V2'
  client_data_json = Base64.urlsafe_encode64(auth.client_data)

  {
    id: auth.key_handle,
    rawId: auth.key_handle,
    clientExtensionResults: { appid: appid },
    type: 'public-key',
    response: {
      clientDataJSON: client_data_json,
      authenticatorData: Base64.urlsafe_encode64(
        CBOR.decode(
          Base64.strict_decode64(authenticator_data)
        )
      ),
      signature: signature
    }
  }
end