Feature: OAuth authentication

Background:
  Given I am in "dotfiles" git repo

Scenario: Ask for username & password, create authorization
  Given the GitHub API server:
    """
    require 'rack/auth/basic'
    get('/authorizations') { '[]' }
    post('/authorizations') {
      auth = Rack::Auth::Basic::Request.new(env)
      halt 401 unless auth.credentials == %w[mislav kitty]
      assert :scopes => ['repo']
      json :token => 'OTOKEN'
    }
    get('/user') {
      halt 401 unless request.env['HTTP_AUTHORIZATION'] == 'token OTOKEN'
      json :login => 'MiSlAv'
    }
    post('/user/repos') {
      halt 401 unless request.env['HTTP_AUTHORIZATION'] == 'token OTOKEN'
      json :full_name => 'mislav/dotfiles'
    }
    """
  When I run %xhub create` interactively
  When I type "mislav"
  And I type "kitty"
  Then the output should contain "github.com username:"
  And the output should contain "github.com password for mislav (never stored):"
  And the exit status should be 0
  And the file "../home/.config/hub" should contain "user: MiSlAv"
  And the file "../home/.config/hub" should contain "oauth_token: OTOKEN"
  And the file "../home/.config/hub" should have mode "0600"

Scenario: Ask for username & password, re-use existing authorization
  Given the GitHub API server:
    """
    require 'rack/auth/basic'
    get('/authorizations') {
      auth = Rack::Auth::Basic::Request.new(env)
      halt 401 unless auth.credentials == %w[mislav kitty]
      json [
        {:token => 'SKIPPD', :note_url => 'http://example.com'},
        {:token => 'OTOKEN', :note_url => 'http://hub.github.com/'}
      ]
    }
    get('/user') {
      json :login => 'mislav'
    }
    post('/user/repos') {
      json :full_name => 'mislav/dotfiles'
    }
    """
  When I run %xhub create` interactively
  When I type "mislav"
  And I type "kitty"
  Then the output should contain "github.com password for mislav (never stored):"
  And the exit status should be 0
  And the file "../home/.config/hub" should contain "oauth_token: OTOKEN"

Scenario: Re-use existing authorization with an old URL
  Given the GitHub API server:
    """
    require 'rack/auth/basic'
    get('/authorizations') {
      auth = Rack::Auth::Basic::Request.new(env)
      halt 401 unless auth.credentials == %w[mislav kitty]
      json [
        {:token => 'OTOKEN', :note => 'hub', :note_url => 'http://defunkt.io/hub/'}
      ]
    }
    post('/authorizations') {
      status 422
      json :message => "Validation Failed",
        :errors => [{:resource => "OauthAccess", :code => "already_exists", :field => "description"}]
    }
    get('/user') {
      json :login => 'mislav'
    }
    post('/user/repos') {
      json :full_name => 'mislav/dotfiles'
    }
    """
  When I run %xhub create` interactively
  When I type "mislav"
  And I type "kitty"
  Then the output should contain "github.com password for mislav (never stored):"
  And the exit status should be 0
  And the file "../home/.config/hub" should contain "oauth_token: OTOKEN"

Scenario: Re-use existing authorization found on page 3
  Given the GitHub API server:
    """
    get('/authorizations') {
      assert_basic_auth 'mislav', 'kitty'
      page = (params[:page] || 1).to_i
      if page < 3
        response.headers['Link'] = %(<#{url}?page=#{page+1}>; rel="next")
        json []
      else
        json [
          {:token => 'OTOKEN', :note => 'hub', :note_url => 'http://hub.github.com/'}
        ]
      end
    }
    post('/authorizations') {
      status 422
      json :message => "Validation Failed",
        :errors => [{:resource => "OauthAccess", :code => "already_exists", :field => "description"}]
    }
    get('/user') {
      json :login => 'mislav'
    }
    post('/user/repos') {
      json :full_name => 'mislav/dotfiles'
    }
    """
  When I run %xhub create` interactively
  When I type "mislav"
  And I type "kitty"
  Then the output should contain "github.com password for mislav (never stored):"
  And the exit status should be 0
  And the file "../home/.config/hub" should contain "oauth_token: OTOKEN"

Scenario: Credentials from GITHUB_USER & GITHUB_PASSWORD
  Given the GitHub API server:
    """
    require 'rack/auth/basic'
    get('/authorizations') {
      auth = Rack::Auth::Basic::Request.new(env)
      halt 401 unless auth.credentials == %w[mislav kitty]
      json [
        {:token => 'OTOKEN', :note_url => 'http://hub.github.com/'}
      ]
    }
    get('/user') {
      json :login => 'mislav'
    }
    post('/user/repos') {
      json :full_name => 'mislav/dotfiles'
    }
    """
  Given $GITHUB_USER is "mislav"
  And $GITHUB_PASSWORD is "kitty"
  When I successfully run %xhub create`
  Then the output should not contain "github.com password for mislav"
  And the file "../home/.config/hub" should contain "oauth_token: OTOKEN"

Scenario: Wrong password
  Given the GitHub API server:
    """
    require 'rack/auth/basic'
    get('/authorizations') {
      auth = Rack::Auth::Basic::Request.new(env)
      halt 401 unless auth.credentials == %w[mislav kitty]
    }
    """
  When I run %xhub create` interactively
  When I type "mislav"
  And I type "WRONG"
  Then the stderr should contain "Error creating repository: Unauthorized (HTTP 401)"
  And the exit status should be 1
  And the file "../home/.config/hub" should not exist

Scenario: Two-factor authentication, create authorization
  Given the GitHub API server:
    """
    require 'rack/auth/basic'
    get('/authorizations') {
      auth = Rack::Auth::Basic::Request.new(env)
      halt 401 unless auth.credentials == %w[mislav kitty]
      if request.env['HTTP_X_GITHUB_OTP'] != "112233"
        response.headers['X-GitHub-OTP'] = "required;application"
        halt 401
      end
      json [ ]
    }
    post('/authorizations') {
      auth = Rack::Auth::Basic::Request.new(env)
      halt 401 unless auth.credentials == %w[mislav kitty]
      halt 412 unless params[:scopes]
      if request.env['HTTP_X_GITHUB_OTP'] != "112233"
        response.headers['X-GitHub-OTP'] = "required;application"
        halt 401
      end
      json :token => 'OTOKEN'
    }
    get('/user') {
      json :login => 'mislav'
    }
    post('/user/repos') {
      json :full_name => 'mislav/dotfiles'
    }
    """
  When I run %xhub create` interactively
  When I type "mislav"
  And I type "kitty"
  And I type "112233"
  Then the output should contain "github.com password for mislav (never stored):"
  Then the output should contain "two-factor authentication code:"
  And the exit status should be 0
  And the file "../home/.config/hub" should contain "oauth_token: OTOKEN"

Scenario: Two-factor authentication, re-use existing authorization
  Given the GitHub API server:
    """
    token = 'OTOKEN'
    post('/authorizations') {
      assert_basic_auth 'mislav', 'kitty'
      token << 'SMS'
      status 412
    }
    get('/authorizations') {
      assert_basic_auth 'mislav', 'kitty'
      if request.env['HTTP_X_GITHUB_OTP'] != "112233"
        response.headers['X-GitHub-OTP'] = "required;application"
        halt 401
      end
      json [ {
        :token => token,
        :note_url => 'http://hub.github.com/'
        } ]
    }
    get('/user') {
      json :login => 'mislav'
    }
    post('/user/repos') {
      json :full_name => 'mislav/dotfiles'
    }
    """
  When I run %xhub create` interactively
  When I type "mislav"
  And I type "kitty"
  And I type "112233"
  Then the output should contain "github.com password for mislav (never stored):"
  Then the output should contain "two-factor authentication code:"
  And the exit status should be 0
  And the file "../home/.config/hub" should contain "oauth_token: OTOKENSMS"

Scenario: Special characters in username & password
  Given the GitHub API server:
    """
    get('/authorizations') { '[]' }
    post('/authorizations') {
      assert_basic_auth 'mislav@example.com', 'my pass@phrase ok?'
      json :token => 'OTOKEN'
    }
    get('/user') {
      json :login => 'mislav'
    }
    get('/repos/mislav/dotfiles') { status 200 }
    """
  When I run %xhub create` interactively
  When I type "mislav@example.com"
  And I type "my pass@phrase ok?"
  Then the output should contain "github.com password for mislav@example.com (never stored):"
  And the exit status should be 0
  And the file "../home/.config/hub" should contain "user: mislav"
  And the file "../home/.config/hub" should contain "oauth_token: OTOKEN"