require 'calabash-cucumber/core' module Calabash module Cucumber # KeychainHelpers provide a helpers to access the iOS keychain. # # ### Simulator Note # # When running on the simulator, the keychain is *not* sandboxed between # applications like it is on a real device. These methods will return # keychain records from *all* applications on the simulator, which may # result in strange behavior if you aren't expecting it. # # @see http://goo.gl/JrFJMM Details about why some operations report # `FAILURE` and what can be done on the client side mitigate. # # @see https://github.com/soffes/sskeychain SSKeychain module KeychainHelpers # @!visibility private # Sends appropriately-configured `GET` request to the `keychain` server # endpoint. Do not call this function directly; use one of the helper # functions provided. # # @see #keychain_accounts # @see #keychain_account_for_service # @return [Array] contents of the iOS keychain # @param [Hash] options # @raise [RuntimeError] if http request does not report success def _keychain_get(options={}) res = http({:method => :get, :raw => true, :path => 'keychain'}, options) res = JSON.parse(res) if res['outcome'] != 'SUCCESS' raise "get keychain with options '#{options}' failed because: '#{res['reason']}'\n'#{res['details']}'" end res['results'] end # Asks the keychain for all of the account records. # # The hash keys are defined by the `SSKeychain` library. # # @see https://github.com/soffes/sskeychain SSKeychain # # The following keys are the most commonly useful: # # ``` # svce #=> the service # acct #=> the account (often a username) # cdat #=> the creation date # mdat #=> the last-modified date # ``` # # @raise [RuntimeError] if http request does not report success # @return [Array] of all account records saved in the iOS keychain. def keychain_accounts _keychain_get end # Returns an list of all account records saved in the iOS keychain # filtered by `service`. # # @see #keychain_accounts # # @param [String] service the service whose accounts you are requesting # @return [Array] a list all account records filtered by `service`. # @raise [RuntimeError] if http request does not report success def keychain_accounts_for_service(service) _keychain_get({:service => service}) end # Ask the keychain for an account password. # # @note # **IMPORTANT** # On the XTC, the password cannot returned as plain text. # When using this keychain_password in your steps you can condition on # the XTC environment using `xamarin_test_cloud?` # # @see Calabash::Cucumber::EnvironmentHelpers#xamarin_test_cloud? # # @raise [RuntimeError] if http request does not report success # @raise [RuntimeError] if `service` and `account` pair does not contain # a password # # @return [String,Array] password stored in keychain for `service` # and `account`. *NB* on the XTC this returns an Array with one Hash. def keychain_password(service, account) _keychain_get({:service => service, :account => account}).first end # @!visibility private # Sends appropriately-configured `POST` request to the `keychain` server # endpoint. Do not call this function directly; use one of the helper # functions provided. # # @see #keychain_clear # @see #keychain_clear_accounts_for_service # @see #keychain_delete_password # @see #keychain_set_password # # @return [nil] # @raise [RuntimeError] if http request does not report success def _keychain_post(options={}) raw = http({:method => :post, :path => 'keychain'}, options) res = JSON.parse(raw) if res['outcome'] != 'SUCCESS' raise "post keychain with options '#{options}' failed because: #{res['reason']}\n#{res['details']}" end nil end # On the iOS Simulator this clears *all* keychain entries for *all* # applications. # # On a physical device, this will clear all entries for the target # application. # # @return [nil] # # @raise [RuntimeError] if http request does not report success def keychain_clear _keychain_post end # Clear all entries in the keychain restricted to a single `service`. # # @param [String] service filters which accounts should be cleared. # @return [nil] # # @raise [RuntimeError] if http request does not report success def keychain_clear_accounts_for_service(service) _keychain_post({:service => service}) end # Delete a single keychain record for the given `service` and `account` # pair. # # @param [String] service filters which accounts should be cleared. # @param [String] account filters which account to clear # # @raise [RuntimeError] if http request does not report success def keychain_delete_password(service, account) _keychain_post(:service => service, :account => account) end # Set the password for a given service and account pair. # # @param [String] service which service to update # @param [String] account which account to update # @param [String] password which password to set # @return nil # # @raise [RuntimeError] if http request does not report success def keychain_set_password(service, account, password) _keychain_post(:service => service, :account => account, :password => password) end end end end