require_relative '../spec_helper'
require_relative './http_authentication_helper'
require 'helpers/unique_names_helper'

describe SignupController do
  include UniqueNamesHelper

  before(:each) do
    ::User.any_instance.stubs(:load_common_data).returns(true)
  end

  describe 'signup page' do
    include_context 'organization with users helper'

    after(:each) do
      @fake_organization.delete if @fake_organization
    end

    it 'returns 200 when subdomainless and route is signup_subdomainless' do
      @fake_organization = create(:organization_whitelist_carto)
      CartoDB.stubs(:subdomainless_urls?).returns(true)
      CartoDB.stubs(:session_domain).returns('localhost.lan')
      Carto::Organization.stubs(:where).returns([@fake_organization])
      host! "localhost.lan"
      get signup_subdomainless_url(user_domain: 'organization')
      response.status.should == 200
    end

    it 'returns 404 when subdomainless and route is signup' do
      @fake_organization = create(:organization_whitelist_carto)
      CartoDB.stubs(:subdomainless_urls?).returns(true)
      CartoDB.stubs(:session_domain).returns('localhost.lan')
      Carto::Organization.stubs(:where).returns([@fake_organization])
      host! "localhost.lan"
      get signup_url
      response.status.should == 404
    end

    it 'returns 404 outside organization subdomains' do
      get signup_url
      response.status.should == 404
      post signup_organization_user_url
      response.status.should == 404
    end

    it 'returns 200 for organizations with signup_page_enabled' do
      @fake_organization = create(:organization_whitelist_carto)
      Carto::Organization.stubs(:where).returns([@fake_organization])
      get signup_url
      response.status.should == 200
    end

    it 'returns 404 for organizations without signup_page_enabled' do
      @fake_organization = create(:organization, whitelisted_email_domains: [])
      Carto::Organization.stubs(:where).returns([@fake_organization])
      get signup_url
      response.status.should == 404
    end

    it 'returns 404 for organizations with whitelisted domains but without any authentication enabled' do
      @fake_organization = create(:organization_whitelist_carto,
                                              auth_username_password_enabled: true,
                                              auth_google_enabled: false,
                                              auth_github_enabled: false)
      @fake_organization.stubs(:auth_username_password_enabled).returns(false)
      Carto::Organization.stubs(:where).returns([@fake_organization])
      get signup_url
      response.status.should == 404
    end

    it 'returns 200 for organizations without signup_page_enabled but with a valid invitation' do
      @fake_organization = create(:organization_with_users, whitelisted_email_domains: [])
      owner = Carto::User.find(@fake_organization.owner.id)
      invitation = Carto::Invitation.create_new(owner, ['wadus@wad.us'], 'Welcome!', false)
      Carto::Organization.stubs(:where).returns([@fake_organization])
      get signup_url(invitation_token: invitation.token('wadus@wad.us'), email: 'wadus@wad.us')
      response.status.should == 200
    end

    it 'returns user error with admin mail if organization has not enough seats' do
      fake_owner = create(:valid_user).carto_user
      @fake_organization = create(:organization_whitelist_carto, seats: 0, owner: fake_owner)
      Carto::Organization.stubs(:where).returns([@fake_organization])

      get signup_url
      response.status.should == 200
      response.body.should include("organization not enough seats")
      response.body.should include("contact the administrator of #{@fake_organization.name}")
    end

    it 'does not return an error if organization has no unassigned_quota left but the invited user is a viewer' do
      email = 'viewer_user@whatever.com'
      @organization.quota_in_bytes = @organization.assigned_quota
      @organization.save
      user = Carto::User.find(@org_user_owner.id)
      invitation = Carto::Invitation.create_new(user, [email], 'W!', true)
      invitation.save
      token = invitation.token(email)

      host! "#{@organization.name}.localhost.lan"
      get signup_organization_user_url(user_domain: @organization.name,
                                       user: { username: 'viewer-user',
                                               email: email,
                                               password: '2{Patrañas}' },
                                               invitation_token: token)

      response.status.should == 200
      response.body.should_not include("quota_in_bytes not enough disk quota")
    end

    it 'returns an error if organization has no unassigned_quota and invited user is not a viewer' do
      email = 'viewer_user@whatever.com'
      @organization.quota_in_bytes = @organization.assigned_quota
      @organization.whitelisted_email_domains = ['whatever.com']
      @organization.save
      user = Carto::User.find(@org_user_owner.id)
      invitation = Carto::Invitation.create_new(user, [email], 'W!', false)
      invitation.save
      token = invitation.token(email)

      host! "#{@organization.name}.localhost.lan"
      get signup_organization_user_url(user_domain: @organization.name,
                                       user: { username: 'viewer-user',
                                               email: email,
                                               password: '2{Patrañas}' },
                                               invitation_token: token)

      response.status.should == 200
      response.body.should include("quota_in_bytes not enough disk quota")
    end

    it 'signs user up as viewer even if all non-viewer seats are taken' do
      @fake_organization = create(:organization_with_users, whitelisted_email_domains: [])
      @fake_organization.seats = @fake_organization.builder_users.count(&:builder?)
      @fake_organization.viewer_seats = 10
      @fake_organization.save
      owner = Carto::User.find(@fake_organization.owner.id)
      invitation = Carto::Invitation.create_new(owner, ['wadus@wad.us'], 'Welcome!', true)
      host! "#{@fake_organization.name}.localhost.lan"
      get signup_url(invitation_token: invitation.token('wadus@wad.us'), email: 'wadus@wad.us')
      response.status.should == 200
    end
  end

  describe 'user creation' do
    include_context 'organization with users helper'

    DEFAULT_QUOTA_IN_BYTES = 1000

    before(:all) do
      @organization.default_quota_in_bytes = DEFAULT_QUOTA_IN_BYTES
      @organization.save

      @org_2_user_owner = TestUserFactory.new.create_owner(@organization_2)
    end

    before(:each) do
      @organization.whitelisted_email_domains = ['carto.com']
      @organization.auth_username_password_enabled = true
      @organization.auth_google_enabled = true
      @organization.strong_passwords_enabled = true
      @organization.save
    end

    it 'triggers validation error and not a NewUser job if email is not valid' do
      ::Resque.expects(:enqueue).never

      username = unique_name('user')
      email = 'testemail@nonono.com'
      password = '2{Patrañas}'
      host! "#{@organization.name}.localhost.lan"
      post signup_organization_user_url(user_domain: @organization.name, user: { username: username, email: email, password: password })
      response.status.should == 422
      Carto::UserCreation.where(username: username).any?.should be_false
    end

    it 'triggers validation error and not a NewUser job if username is too long' do
      ::Resque.expects(:enqueue).never

      name = 'sixtythreecharacterslongiswaytoomanycharactersmatewhydoyoueventry'
      email = "testemail@#{@organization.whitelisted_email_domains[0]}"
      password = '00012345678'
      user = { username: name, email: email, password: password }
      org_name = @organization.name
      host! "#{@organization.name}.localhost.lan"
      post signup_organization_user_url(user_domain: org_name, user: user)
      response.status.should == 422
      Carto::UserCreation.where(username: name).any?.should be_false
    end

    it 'triggers validation error is password is too short' do
      user = ::User.new

      user.username = 'testusername'
      user.email = 'manolo@escobar.es'
      user.password = 'short'
      user.password_confirmation = user.password

      user.errors[:password].blank?.should == false
    end

    it 'returns 400 error if you attempt username + password signup and it is not valid' do
      @organization.auth_username_password_enabled = false
      @organization.save

      host! "#{@organization.name}.localhost.lan"
      post signup_organization_user_url(user_domain: @organization.name, user: { username: 'anewuser', email: "anewuser@#{@organization.whitelisted_email_domains.first}", password: 'password' })
      response.status.should == 400
      ::Resque.expects(:enqueue).never
    end

    it 'triggers a NewUser job with form parameters and default quota and requiring validation email' do
      ::Resque.expects(:enqueue).with(::Resque::UserJobs::Signup::NewUser,
                                      instance_of(String), anything, instance_of(FalseClass)).returns(true)

      username = 'testusername'
      email = "testemail@#{@organization.whitelisted_email_domains[0]}"
      password = '2{Patrañas}'
      host! "#{@organization.name}.localhost.lan"
      post signup_organization_user_url(user_domain: @organization.name, user: { username: username, email: email, password: password })
      response.status.should == 200
      last_user_creation = Carto::UserCreation.order('created_at desc').limit(1).first
      last_user_creation.username.should == username
      last_user_creation.email.should == email
      last_user_creation.crypted_password.should_not be_empty
      last_user_creation.organization_id.should == @organization.id
      last_user_creation.quota_in_bytes.should == @organization.default_quota_in_bytes
      last_user_creation.requires_validation_email?.should == true
      last_user_creation.created_via.should == Carto::UserCreation::CREATED_VIA_ORG_SIGNUP
    end

    it 'trigger creation if mail is whitelisted with wildcard' do
      @organization.whitelisted_email_domains = ['*.carto.com']
      @organization.save
      ::Resque.expects(:enqueue).with(::Resque::UserJobs::Signup::NewUser,
                                      instance_of(String), anything, instance_of(FalseClass)).returns(true)
      username = 'testusername'
      email = "testemail@a.carto.com"
      password = '2{Patrañas}'
      host! "#{@organization.name}.localhost.lan"
      post signup_organization_user_url(user_domain: @organization.name, user: { username: username, email: email, password: password })
      response.status.should == 200
      last_user_creation = Carto::UserCreation.order('created_at desc').limit(1).first
      last_user_creation.username.should == username
      last_user_creation.email.should == email
      last_user_creation.crypted_password.should_not be_empty
      last_user_creation.organization_id.should == @organization.id
      last_user_creation.quota_in_bytes.should == @organization.default_quota_in_bytes
      last_user_creation.requires_validation_email?.should == true
      last_user_creation.created_via.should == Carto::UserCreation::CREATED_VIA_ORG_SIGNUP
    end

    it 'Returns 422 for not whitelisted domains' do
      ::Resque.expects(:enqueue).never

      host! "#{@organization.name}.localhost.lan"
      post signup_organization_user_url(user_domain: @organization.name,
                                        user: { username: 'evil', email: 'evil@whatever.com', password: '2{Patrañas}' })
      response.status.should == 422
    end

    it "doesn't trigger creation if mail domain is not whitelisted and invitation token is wrong" do
      ::Resque.stubs(:enqueue)
      ::Resque.expects(:enqueue).
        with(::Resque::UserJobs::Signup::NewUser, anything, anything, anything).
        never
      invited_email = 'invited_user@whatever.com'
      invitation = Carto::Invitation.create_new(Carto::User.find(@org_user_owner.id), [invited_email], 'W!', false)
      invitation.save

      host! "#{@organization.name}.localhost.lan"
      post signup_organization_user_url(
        user_domain: @organization.name,
        user: { username: 'invited-user', email: invited_email, password: '2{Patrañas}' },
        invitation_token: 'wrong'
      )
      response.status.should == 400
      invitation.reload
      invitation.used_emails.should_not include(invited_email)
    end

    it 'returns 400 if invitation token is for a different organization' do
      invited_email = 'invited_user@whatever.com'
      invitation = Carto::Invitation.create_new(Carto::User.find(@org_2_user_owner.id), [invited_email], 'W!', false)
      invitation.save

      ::Resque.expects(:enqueue).
        with(::Resque::UserJobs::Signup::NewUser, instance_of(String), anything, instance_of(FalseClass)).
        never
      host! "#{@organization.name}.localhost.lan"
      post signup_organization_user_url(user_domain: @organization.name,
                                        user: { username: 'invited-user',
                                                email: invited_email,
                                                password: '2{Patrañas}' },
                                        invitation_token: invitation.token(invited_email))
      response.status.should == 400
      invitation.reload
      invitation.used_emails.should_not include(invited_email)
    end

    it 'triggers creation without validation email spending an invitation even if mail domain is not whitelisted' do
      invited_email = 'invited_user@whatever.com'
      invitation = Carto::Invitation.create_new(Carto::User.find(@org_user_owner.id), [invited_email], 'W!', false)
      invitation.save

      ::Resque.expects(:enqueue).
        with(::Resque::UserJobs::Signup::NewUser, instance_of(String), anything, instance_of(FalseClass)).
        returns(true)
      host! "#{@organization.name}.localhost.lan"
      post signup_organization_user_url(user_domain: @organization.name,
                                        user: { username: 'invited-user',
                                                email: invited_email,
                                                password: '2{Patrañas}' },
                                        invitation_token: invitation.token(invited_email))
      response.status.should == 200
      last_user_creation = Carto::UserCreation.order('created_at desc').limit(1).first
      @organization.whitelisted_email_domains.should_not include(last_user_creation.email)
      last_user_creation.organization_id.should == @organization.id
      last_user_creation.requires_validation_email?.should == false
      invitation.reload
      invitation.used_emails.should include(invited_email)
    end

    it 'triggers creation invitation twice' do
      invited_email = 'invited_user@whatever.com'

      Carto::Invitation.create_new(
        Carto::User.find(@org_user_owner.id), [invited_email], 'W!', false
      )
      invitation = Carto::Invitation.create_new(
        Carto::User.find(@org_user_owner.id), [invited_email], 'W!', false
      )
      invitation.save

      ::Resque.expects(:enqueue).with(
        ::Resque::UserJobs::Signup::NewUser,
        instance_of(String),
        anything,
        instance_of(FalseClass)
      ).returns(true)

      host! "#{@organization.name}.localhost.lan"
      post signup_organization_user_url(
        user_domain: @organization.name,
        user: { username: 'invited-user',
                email: invited_email,
                password: '2{Patrañas}' },
        invitation_token: invitation.token(invited_email)
      )

      invitation.reload
      invitation.used_emails.should include(invited_email)
    end

    it 'triggers a viewer creation that creates a viewer user' do
      invited_email = 'viewer_user@whatever.com'
      invitation = Carto::Invitation.create_new(Carto::User.find(@org_user_owner.id), [invited_email], 'W!', true)
      invitation.save
      ::Resque.expects(:enqueue).
        with(::Resque::UserJobs::Signup::NewUser, instance_of(String), anything, instance_of(FalseClass)).
        returns(true)

      host! "#{@organization.name}.localhost.lan"
      post signup_organization_user_url(user_domain: @organization.name,
                                        user: { username: 'viewer-user',
                                                email: invited_email,
                                                password: '2{Patrañas}' },
                                        invitation_token: invitation.token(invited_email))
      last_user_creation = Carto::UserCreation.order('created_at desc').limit(1).first
      last_user_creation.next_creation_step until last_user_creation.finished?

      last_user_creation.state.should eq 'success'
      last_user_creation.viewer.should be_true
      last_user_creation.user.viewer.should be_true
    end

    describe 'ldap signup' do

      before(:all) do
        @ldap_configuration = create(:ldap_configuration, { organization_id: @organization.id })
      end

      after(:all) do
        @ldap_configuration.destroy
      end

      it 'returns 404 if ldap is enabled' do

        get signup_organization_user_url(user_domain: @organization.name)
        response.status.should == 404

        post signup_organization_user_url(user_domain: @organization.name, user: { username: 'whatever', email: 'whatever@carto.com', password: 'whatever' })
        response.status.should == 404
      end

    end

    describe 'http authentication signup' do
      include HttpAuthenticationHelper

      describe 'header authentication disabled' do
        it 'returns 404 if http authentication is not set' do
          stub_http_header_authentication_configuration(enabled: false)
          get signup_http_authentication_url
          response.status.should == 404
        end

        it 'returns 404 if http authentication autocreation is disabled' do
          stub_http_header_authentication_configuration(autocreation: false)
          get signup_http_authentication_url
          response.status.should == 404
        end
      end

      describe 'header authentication enabled' do
        it 'returns 404 if http authentication autocreation is disabled' do
          stub_http_header_authentication_configuration(autocreation: false)
          get signup_http_authentication_url
          response.status.should == 404
        end

        it 'returns 500 if http authentication is not set to email' do
          ['auto', 'id', 'username'].each do |field|
            stub_http_header_authentication_configuration(autocreation: true, field: field)
            get signup_http_authentication_url
            response.status.should == 500
          end
        end

        describe 'autocreation enabled' do
          before(:each) do
            stub_http_header_authentication_configuration(autocreation: true)
          end

          it 'returns 403 if http authentication header is not present' do
            get signup_http_authentication_url
            response.status.should == 403
          end

          it 'triggers user creation without organization' do
            email = 'authenticated@whatever.com'

            ::Resque.expects(:enqueue).
              with(::Resque::UserJobs::Signup::NewUser, instance_of(String), anything, instance_of(FalseClass)).
              returns(true)
            get signup_http_authentication_url, {}, authentication_headers(email)
            response.status.should == 200

            last_user_creation = Carto::UserCreation.order('created_at desc').limit(1).first
            last_user_creation.organization_id.should == nil
            last_user_creation.requires_validation_email?.should == false
          end

          it 'triggers user creation with organization' do
            username = "authenticated"
            email = "#{username}@#{@organization.whitelisted_email_domains.first}"

            ::Resque.expects(:enqueue).
              with(::Resque::UserJobs::Signup::NewUser, instance_of(String), anything, instance_of(FalseClass)).
              returns(true)

            host! "#{@organization.name}.localhost.lan"
            get signup_http_authentication_url(user_domain: @organization.name), {}, authentication_headers(email)
            response.status.should == 200

            last_user_creation = Carto::UserCreation.order('created_at desc').limit(1).first
            last_user_creation.organization_id.should == @organization.id
            last_user_creation.requires_validation_email?.should == false
            last_user_creation.username.should == username
            last_user_creation.email.should == email
            last_user_creation.crypted_password.should_not be_empty
          end
        end
      end
    end
  end
end
