<commit_before>require 'tmpdir'
require 'webrick'
require_relative 'spec_helper'

module LetsCert

  describe Certificate do

    before(:all) { Certificate.logger = Logger.new('/dev/null') }

    before(:all) do
      # Generate a signed certtificate and its associated domains
      @cert, @domains = generate_signed_cert

      # minimum size accepted by ACME server
      @account_key2048 = OpenSSL::PKey::RSA.new(2048)

      # Create temporary directory
      @tmpdir = Dir.mktmpdir('test_letscert')

      # Define expected exception when no server defined
      @no_server_exception = case RUBY_VERSION.sub(/\.\d+$/, '')
                             when '2.1'
                               URI::InvalidURIError
                             else
                               Faraday::ConnectionFailed
                             end
    end

    after(:all) do
      # Remove temporary directory
      system "rm -rf #@tmpdir"
    end

    let(:certificate) { Certificate.new(@cert) }
    let(:options) { { roots: { 'example.com' => @tmpdir },
                      server: LetsCert::TEST::SERVER,
                      email: '<EMAIL>',
                      account_key_type: 'ecdsa',
                      account_key_size: 256,
                      cert_rsa: 2048 } }

    context '#get' do

      it 'checks all domains have a root' do
        runner = Runner.new
        ARGV.clear

        ARGV << '-d' << 'example.com:/var/ww/html'
        ARGV << '--server' << TEST::SERVER
        runner.parse_options
        VCR.use_cassette('single-domain') do
          # raise error because no e-mail address was given
          expect { certificate.get(nil, nil, runner.options) }.
            to raise_error(Acme::Client::Error)
        end

        ARGV.clear
        ARGV << '-d' << 'example.com:/var/www/html'
        ARGV << '-d' << 'www.example.com'
        ARGV << '--server' << TEST::SERVER
        runner.options[:domains] = []
        runner.parse_options
        expect { certificate.get(nil, nil, runner.options) }.
          to raise_error(LetsCert::Error).
              with_message(/not specified: www\.example\.com\./)

        ARGV.clear
        ARGV << '-d' << 'example.com:/var/www/html'
        ARGV << '-d' << 'www.example.com'
        ARGV << '--default-root' << '/opt/www'
        ARGV << '--server' << TEST::SERVER
        runner.parse_options
        VCR.use_cassette('default-root') do
          # raise error because no e-mail address was given
          expect { certificate.get(nil, nil, runner.options) }.
            to raise_error(Acme::Client::Error)
        end
        expect(runner.options[:roots]['example.com']).to eq('/var/www/html')
        expect(runner.options[:roots]['www.example.com']).to eq('/opt/www')
      end

      it 'uses existing account key' do
        opts = { roots: options[:roots] }

        VCR.use_cassette('no-server') do
          # Connection error: no server to connect to
          expect { certificate.get(@account_key2048, nil, opts) }.
            to raise_error(@no_server_exception)
        end
        expect(certificate.client.jwk.to_h[:n]).to eq(jwk_encode(@account_key2048.n.to_s(2)))
      end

      it 'creates an ACME account key if none exists' do
        opts = {
          roots: options[:roots],
          account_key_type: 'rsa',
          account_key_size: 128,
        }

        VCR.use_cassette('no-server') do
          # Connection error: no server to connect to
          expect { certificate.get(nil, nil, opts) }.
            to raise_error(@no_server_exception)
        end
        expect(certificate.client.jwk).to be_a(Acme::Client::JWK::RSA)
      end

      it 'creates an ACME client with provided account key and end point' do
        VCR.use_cassette('create-acme-client') do
          # Acme error: e-mail address not valid
          expect { certificate.get(@account_key2048, nil, options) }.
            to raise_error(Acme::Client::Error)
        end
        expect(certificate.client.jwk.to_h[:n]).to eq(jwk_encode(@account_key2048.n.to_s(2)))
        expect(certificate.client.instance_eval { @endpoint }).to eq(options[:server])
      end

      it 'raises when register without e-mail' do
        options.delete :email
        VCR.use_cassette('create-acme-client-but-bad-email') do
          # Acme error: not valid e-mail address
          expect { certificate.get(@account_key2048, nil, options) }.
            to raise_error(Acme::Client::Error).
                with_message('not a valid e-mail address')
        end
      end

      it 'responds to HTTP-01 challenge' do
        VCR.use_cassette('http-01-challenge') do
          serve_files_from @tmpdir do
            certificate.get(@account_key2048, nil, options)
          end
        end
        expect(certificate.cert).to_not eq(@cert)
      end

      it 'responds to HTTP-01 challenge with a ECDSA account key' do
        VCR.use_cassette('http-01-challenge-ecdsa-account-key') do
          serve_files_from @tmpdir do
            certificate.get(nil, nil, options)
          end
        end
        expect(certificate.cert).to_not eq(@cert)
      end

      it 'raises if HTTP-01 challenge is unavailable' do
        VCR.use_cassette('no-http-01-challenge') do
          certificate.get_acme_client(@account_key2048, options) do |client|
            client.connection.builder.insert 0, HttpHelper::RemoveHttp01Middleware
          end
          expect { certificate.get(@account_key2048, nil, options) }.
            to raise_error(LetsCert::Error).with_message(/not offer http-01/)
        end
      end

      it 'creates a new private key if --reuse-key is not present' do
        options[:files] = %w(fake)
        key = OpenSSL::PKey::RSA.new(TEST::KEY_LENGTH)

        VCR.use_cassette('http-01-challenge') do
          serve_files_from @tmpdir do
            certificate.get(@account_key2048, key, options)
          end
        end
        expect(certificate.cert).to_not eq(@cert)
        expect(IOPluginHelper::FakeIOPlugin.saved_data[:key]).to_not eq(key)
      end

      it 'reuses existing private key if --reuse-key is present' do
        options[:files] = %w(fake)
        options[:reuse_key] = true
        key = OpenSSL::PKey::RSA.new(TEST::KEY_LENGTH)
<commit_msg>Update 'Certificate#get responds to HTTP-01 challenge with a ECDSA account key' spec to ensure generated account key is an ECDSA one.<commit_after>require 'tmpdir'
require 'webrick'
require_relative 'spec_helper'

module LetsCert

  describe Certificate do

    before(:all) { Certificate.logger = Logger.new('/dev/null') }

    before(:all) do
      # Generate a signed certtificate and its associated domains
      @cert, @domains = generate_signed_cert

      # minimum size accepted by ACME server
      @account_key2048 = OpenSSL::PKey::RSA.new(2048)

      # Create temporary directory
      @tmpdir = Dir.mktmpdir('test_letscert')

      # Define expected exception when no server defined
      @no_server_exception = case RUBY_VERSION.sub(/\.\d+$/, '')
                             when '2.1'
                               URI::InvalidURIError
                             else
                               Faraday::ConnectionFailed
                             end
    end

    after(:all) do
      # Remove temporary directory
      system "rm -rf #@tmpdir"
    end

    let(:certificate) { Certificate.new(@cert) }
    let(:options) { { roots: { 'example.com' => @tmpdir },
                      server: LetsCert::TEST::SERVER,
                      email: '<EMAIL>',
                      account_key_type: 'ecdsa',
                      account_key_size: 256,
                      cert_rsa: 2048 } }

    context '#get' do

      it 'checks all domains have a root' do
        runner = Runner.new
        ARGV.clear

        ARGV << '-d' << 'example.com:/var/ww/html'
        ARGV << '--server' << TEST::SERVER
        runner.parse_options
        VCR.use_cassette('single-domain') do
          # raise error because no e-mail address was given
          expect { certificate.get(nil, nil, runner.options) }.
            to raise_error(Acme::Client::Error)
        end

        ARGV.clear
        ARGV << '-d' << 'example.com:/var/www/html'
        ARGV << '-d' << 'www.example.com'
        ARGV << '--server' << TEST::SERVER
        runner.options[:domains] = []
        runner.parse_options
        expect { certificate.get(nil, nil, runner.options) }.
          to raise_error(LetsCert::Error).
              with_message(/not specified: www\.example\.com\./)

        ARGV.clear
        ARGV << '-d' << 'example.com:/var/www/html'
        ARGV << '-d' << 'www.example.com'
        ARGV << '--default-root' << '/opt/www'
        ARGV << '--server' << TEST::SERVER
        runner.parse_options
        VCR.use_cassette('default-root') do
          # raise error because no e-mail address was given
          expect { certificate.get(nil, nil, runner.options) }.
            to raise_error(Acme::Client::Error)
        end
        expect(runner.options[:roots]['example.com']).to eq('/var/www/html')
        expect(runner.options[:roots]['www.example.com']).to eq('/opt/www')
      end

      it 'uses existing account key' do
        opts = { roots: options[:roots] }

        VCR.use_cassette('no-server') do
          # Connection error: no server to connect to
          expect { certificate.get(@account_key2048, nil, opts) }.
            to raise_error(@no_server_exception)
        end
        expect(certificate.client.jwk.to_h[:n]).to eq(jwk_encode(@account_key2048.n.to_s(2)))
      end

      it 'creates an ACME account key if none exists' do
        opts = {
          roots: options[:roots],
          account_key_type: 'rsa',
          account_key_size: 128,
        }

        VCR.use_cassette('no-server') do
          # Connection error: no server to connect to
          expect { certificate.get(nil, nil, opts) }.
            to raise_error(@no_server_exception)
        end
        expect(certificate.client.jwk).to be_a(Acme::Client::JWK::RSA)
      end

      it 'creates an ACME client with provided account key and end point' do
        VCR.use_cassette('create-acme-client') do
          # Acme error: e-mail address not valid
          expect { certificate.get(@account_key2048, nil, options) }.
            to raise_error(Acme::Client::Error)
        end
        expect(certificate.client.jwk.to_h[:n]).to eq(jwk_encode(@account_key2048.n.to_s(2)))
        expect(certificate.client.instance_eval { @endpoint }).to eq(options[:server])
      end

      it 'raises when register without e-mail' do
        options.delete :email
        VCR.use_cassette('create-acme-client-but-bad-email') do
          # Acme error: not valid e-mail address
          expect { certificate.get(@account_key2048, nil, options) }.
            to raise_error(Acme::Client::Error).
                with_message('not a valid e-mail address')
        end
      end

      it 'responds to HTTP-01 challenge' do
        VCR.use_cassette('http-01-challenge') do
          serve_files_from @tmpdir do
            certificate.get(@account_key2048, nil, options)
          end
        end
        expect(certificate.cert).to_not eq(@cert)
      end

      it 'responds to HTTP-01 challenge with a ECDSA account key' do
        VCR.use_cassette('http-01-challenge-ecdsa-account-key') do
          serve_files_from @tmpdir do
            certificate.get(nil, nil, options)
          end
        end
        expect(certificate.cert).to_not eq(@cert)
        expect(certificate.client.jwk).to be_a(Acme::Client::JWK::ECDSA)
        expect(certificate.client.jwk.jwa_alg).to eq('ES256')
      end

      it 'raises if HTTP-01 challenge is unavailable' do
        VCR.use_cassette('no-http-01-challenge') do
          certificate.get_acme_client(@account_key2048, options) do |client|
            client.connection.builder.insert 0, HttpHelper::RemoveHttp01Middleware
          end
          expect { certificate.get(@account_key2048, nil, options) }.
            to raise_error(LetsCert::Error).with_message(/not offer http-01/)
        end
      end

      it 'creates a new private key if --reuse-key is not present' do
        options[:files] = %w(fake)
        key = OpenSSL::PKey::RSA.new(TEST::KEY_LENGTH)

        VCR.use_cassette('http-01-challenge') do
          serve_files_from @tmpdir do
            certificate.get(@account_key2048, key, options)
          end
        end
        expect(certificate.cert).to_not eq(@cert)
        expect(IOPluginHelper::FakeIOPlugin.saved_data[:key]).to_not eq(key)
      end

      it 'reuses existing private key if --reuse-key is present' do
        options[:files] = %w(fake)
        options[:reuse_key] = true
        key = OpenSSL::PKey::RSA.new(TEST::KEY_LENGTH)

{'fraction_non_alphanumeric': 0.11579029611688413, 'fraction_numerical': 0.012422845534807407, 'mean_word_length': 2.087313072841293, 'pattern_counts': {'":': 0, '<': 77, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 18, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '738973', 'n_tokens_mistral': 4016, 'n_tokens_neox': 3783, 'n_words': 874}