Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finalize pwfilesupport #25

Merged
merged 34 commits into from Mar 9, 2017
Merged

Conversation

varet80
Copy link
Contributor

@varet80 varet80 commented Feb 28, 2017

New Feature: Create a Passwd file using Databags

- Generate UserDB
 - Source from databags
 - Compare users with the current if exist

- Kitchen
 - Change Exceptions for centos 7.2 (fall into some bugs)
 - Add databags support
 - Add databags with two users

- User Recipe
 - Change priority on dovecot user creation (group goes after user)

- New AttribugesAttributes
 - Databag name attribute is  `databag_name`
 - Databag item for userdb `databag_users_item`

stromp and others added 28 commits January 12, 2016 17:22
…cot-cookbook into adding_pwfilesupport

Conflicts:
	attributes/conf_files.rb
- Generate UserDB
 - Source from databags
 - Compare users with the current if exist

- Kitchen
 - Change Exceptions for centos 7.2 (fall into some bugs)
 - Add databags support
 - Add databags with two users

- User Recipe
 - Change priority on dovecot user creation (group goes after user)

- Rename Attributes
 - Databag attribute is now `databag_name` (more generic)
 - Databag item for users is now `databag_users_item`
@varet80
Copy link
Contributor Author

varet80 commented Mar 2, 2017

Hey! thanks for the support

  1. I have already done the new recipe
  2. databags too
  3. I need to add the recipe on a new suite
  4. test the new suite
  5. Move the block under custom resource or create library for it... thinking about it(too big for ruby block)
    redoo 2-4

Copy link
Owner

@zuazo zuazo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Thanks @billiaz! I added some comments to your code.

Don't worry too much about Custom Resources, that may be too much to be included in this PR. We can address it in a future issue/PR anyway.

"#{node['dovecot']['conf_path']}/password"

default['dovecot']['conf']['password_file'] = \
"#{node['dovecot']['conf_path']}/password"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same attribute set twice here.

metadata.rb Outdated
@@ -27,7 +27,7 @@
Installs and configures Dovecot, open source IMAP and POP3 email server.
EOH
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
version '3.2.0' # WiP
version '3.2.0001'
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid changing this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted

README.md Outdated

Creates and configures a password file from local mailboxes based on a data bag.

* `node['dovecot']['databag_users_item']`: The databag item to use (under the databag set)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add databag_name attribute here?

Also, adding a example content of the data bag would make its usage easier. The same you used for the tests will be enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it better to have it:
node['dovecot']['databag_name']['users_item'] ?

#
extend Chef::Mixin::ShellOut

include_recipe 'dovecot::from_package'
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to use include_recipe "dovecot::from_#{node['dovecot']['install_from']}" here as in the ::default recipe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will update it on the next commit, Thnx

recipes/user.rb Outdated
else
user node['dovecot']['conf']['default_login_user']
group node['dovecot']['conf']['default_login_user']
end
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is wrong here. It is not inside a resource block.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to check this, it was inherited.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

legacy from the previous pull request, cleaning it and testing it

variables(
credentials: credentials
)
only_if { credentials_updated }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add sensitive true property to this resource.

[crypt, uid, gid, gecos, homedir, shell, extra_fields]
else
[crypt]
end
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of that if? Sorry, I don't quite understand it 😞

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refactored :) now inside libs
this accepts any type of userdb entry
there have been 2 ways:
user:pass (old)
current:
user:pass:uid:gid:gecos:homedir:shell:extra < some of these doesn't work
this is to force importing properly both, but now after I made some changes it should work better, with less complexity

Refactoring User Creation
 - Use library with small functions
 - Add new simpler way of managing the changes
 - Add documentation and examples

Tests
 - Add unit testing
 - New kitchen suite
 - Server specs for the new kitchen suite

Rubocop:
 - Make lines wider
 - Fix Style syntax
 - Add travis checks for the new kitchen unit tests
 - Fix readme spaces under apache license
README.md Outdated
http://www.apache.org/licenses/LICENSE-2.0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixing it on my next commit with the style changes :(
my lovely editor removes all white space

Added kitchen and travis support
- Unfortunatelly not every test is completed as the config changes per
  dovecot version. (To be done on next versions)
- Newer versions require ssl (especially on latest Deb/Ub)
@coveralls
Copy link

Coverage Status

Coverage increased (+0.03%) to 98.645% when pulling 469a453 on schubergphilis:finalize_pwfilesupport into a57e307 on zuazo:master.

2 similar comments
@coveralls
Copy link

Coverage Status

Coverage increased (+0.03%) to 98.645% when pulling 469a453 on schubergphilis:finalize_pwfilesupport into a57e307 on zuazo:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.03%) to 98.645% when pulling 469a453 on schubergphilis:finalize_pwfilesupport into a57e307 on zuazo:master.

Copy link
Owner

@zuazo zuazo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@billiaz astonishing work! ✨

I have commented some possible improvements and added some notes to help you complete the unit tests.

@@ -23,6 +23,8 @@
default['dovecot']['conf_files_user'] = 'root'
default['dovecot']['conf_files_group'] = node['dovecot']['group']
default['dovecot']['conf_files_mode'] = '00644'
default['dovecot']['conf']['password_file'] = \
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The \ at the en of this line is not needed.


def self.exists?(localdata)
true if ::File.exist?(localdata)
end
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK you don't need an if there:

def self.exists?(localdata)
  ::File.exist?(localdata)
end


def self.file_to_hash(inputfile)
output_entries = {}
passwordfile = File.open(inputfile, File::RDONLY | File::CREAT, 640)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To use octal numbers, you must add a 0 before the number. So, we need to use 0640 here.

end
passwordfile.close
output_entries
end
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this method can be simplified as follows:

def self.file_to_hash(inputfile)
  File.open(inputfile, File::RDONLY | File::CREAT, 0640) do |passwordfile|
    passwordfile.readlines.each_with_object({}) do |line, output_entries|
      user, data = fileline_to_userdb_hash(line)
      output_entries[user] = data
    end
  end
end

def self.password_valid?(hashed_pw, plaintext_pw)
true unless shell_out(
"/usr/bin/doveadm pw -t '#{hashed_pw}' -p '#{plaintext_pw}'"
).exitstatus != 0
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same here, no need for the unless:

def self.password_valid?(hashed_pw, plaintext_pw)
  shell_out("/usr/bin/doveadm pw -t '#{hashed_pw}' -p '#{plaintext_pw}'")
    .exitstatus == 0
end

variables(
credentials: credentials
)
only_if { update_credentials }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a sensitive true here 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is added on my next commit ;)

it { should connect }
it { should authenticate 'vassilis', 'vassilis1234' }
it { should authenticate 'dilan', 'password1234' }
it { should_not authenticate 'vassilis', 'vassilis123' }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

default['dovecot']['conf']['ssl'] = \
if node['platform_family'] == 'suse' && node['platform_version'].to_i < 13
false
end
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

.with_path('/etc/dovecot/password')
.with_source('password.erb')
end
end
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is failing:

Failures:

  1) dovecot::create_pwfile when the module runs creates a file
     Failure/Error:
       expect(chef_run). to create_template('/etc/dovecot/password')
         .with_path('/etc/dovecot/password')
         .with_source('password.erb')
       expected "template[/etc/dovecot/password]" actions [] to include :create

     # ./test/unit/recipes/create_pwfile_spec.rb:29:in `block (3 levels) in <top (required)>'

That's because we need to run the code inside the ruby_block and force it to set update_credentials to true.

It is not easy to understand, but here you have a working version of ChefSpec tests for the create_pwfile recipe:

describe 'dovecot::create_pwfile', order: :random do
  let(:chef_runner) { ChefSpec::SoloRunner.new }
  let(:chef_run) { chef_runner.converge(described_recipe) }
  let(:node) { chef_runner.node }

  it 'runs databag_to_dovecot_userdb ruby block' do
    expect(chef_run).to run_ruby_block('databag_to_dovecot_userdb')
  end

  describe 'inside ruby_block[databag_to_dovecot_userdb] resource' do
    let(:chef_runner) { ChefSpec::SoloRunner.new(step_into: %w(ruby_block)) }
    let(:data_bag_users) do
      {
        'users' => {
          'dilan' => 'password1234',
          'vassilis' => ['vassilis1234', nil, nil, nil, nil, nil, nil]
        }
      }
    end
    let(:pwfile) { instance_double('File', readlines: [], close: nil) }
    let(:encrypted_password) { 'encrypted_password' }
    before do
      # Stub ::File.open:
      allow(::File).to receive(:open).and_call_original
      allow(::File).to receive(:open).with('/etc/dovecot/password', any_args)
        .and_return(pwfile)
      # If passing a block with do...end to ::File.open:
      # allow(::File).to receive(:open).with('/etc/dovecot/password', any_args)
      #   .and_yield(pwfile)
  
      # Stub Data Bag:
      stub_data_bag_item('dovecot', 'users').and_return(data_bag_users)
  
      # Stub some commands excuted inside the ruby_block:
      # Using: https://github.com/zuazo/filesystem_resize-cookbook/blob/master/test/unit/support/stubs.rb
      stub_shell_out(
        '/usr/bin/doveadm pw -s MD5 -p                 password1234'
      ).and_return_stdout(encrypted_password)
      stub_shell_out(
        '/usr/bin/doveadm pw -s MD5 -p                 vassilis1234'
      ).and_return_stdout(encrypted_password)
    end

    it 'creates password file template' do
      expect(chef_run).to create_template('/etc/dovecot/password')
        .with_path('/etc/dovecot/password')
        .with_source('password.erb')
        .with_owner('dovecot')
        .with_group('dovecot')
        .with_mode('0640')
        # .with_sensitve(true)
    end
  end
end

This example uses the stub_data_bag_item function from here. You need to copy that file to the test/unit/support directory and include it in the test/unit/spec_helper.rb file.

If you change the ::File.open call to use a block, as I have recommended you above, you need to uncomment the line with the and_yield.

Of course, feel free to ask me any questions about this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unit tests... the worst for me :( I cannot get it yet (though pretty new with these concepts)

@varet80
Copy link
Contributor Author

varet80 commented Mar 8, 2017

Thanks for the help! it is my first real contribution.

- Update recipes with more efficient code
- Use the awesome Unit test made by the Maintainer
@coveralls
Copy link

coveralls commented Mar 8, 2017

Coverage Status

Coverage decreased (-5.6%) to 92.966% when pulling 39d13f8 on schubergphilis:finalize_pwfilesupport into a57e307 on zuazo:master.

@varet80
Copy link
Contributor Author

varet80 commented Mar 8, 2017

Is there something more I should do to make this pull request ready for merge?

@zuazo
Copy link
Owner

zuazo commented Mar 9, 2017

Thanks @billiaz. No, it is ready to be merged. Give me a couple of days because I want to test some of the functions by hand. I'm planning to release this during weekend if you can wait a little.

zuazo added a commit that referenced this pull request Mar 9, 2017
@zuazo zuazo merged commit 39d13f8 into zuazo:master Mar 9, 2017
@zuazo
Copy link
Owner

zuazo commented Mar 9, 2017

OK. I have merged this PR after:

  • Adding some unit tests for the new library.
  • Documented the new functions.
  • Changed the code a little to avoid disabling RuboCop's LineLength.

You can see the changes in 667058b.

@zuazo
Copy link
Owner

zuazo commented Mar 10, 2017

Released in 3.2.0.

Thanks for your patience.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants