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

New Custom Resource: Certificate and Library: SecurityCommand #53

Merged
merged 14 commits into from
Mar 5, 2018

Conversation

mjmerin
Copy link
Member

@mjmerin mjmerin commented Mar 1, 2018

This pull request fixes Issue #50. Rather than create a security resource, the task was split up to create a security back end to drive other resources such as a certificate and even a keychain resource.

SecurityCommand Library
Basic library intended to encapsulate some features of the security command. As of now this will allow you to unlock a keychain, and add a certificate of the .p12 or .cer format.

Certificate Custom Resource
This is a new custom resource that utilizes the SecurityCommand library mentioned above as its backend. It currently only has 3 properties for basic certificate install.

Notes

  • For testing, a PFX format Test.p12 has been added. This is a dummy certificate file

- Split up smoke tests for easier testing
Use the security command to install a certificate

Use 3 properties and a simple if else statement to differentiate
between .cer and .p12 files
- Add new recipe installing dummy .p12 file
Use some functions that are modeled after the actual security command
such as unlock-keychain, add-certificates, and import.

There is also a install_certificate wrapper that wraps the import and
add-certificate commands to hide the differences of .p12 and .cer file
installation from the user.
@mjmerin mjmerin self-assigned this Mar 1, 2018
[@security, 'unlock-keychain', '-p', kc_passwd]
end

def add_certificates
Copy link
Collaborator

@americanhanko americanhanko Mar 1, 2018

Choose a reason for hiding this comment

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

Let's use a ternary statement here in the interest of brevity:

@keychain == '' ? [@security, 'add-certificates', @cert] : [@security, 'add-certificates', @cert, '-k', @keychain]

Copy link
Member Author

Choose a reason for hiding this comment

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

done

property :keychain, String

action_class do
def keychain
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ternary statement here as well:

property_is_set?(:keychain) ? new_resource.keychain : ''

Copy link
Member Author

Choose a reason for hiding this comment

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

done

test = SecurityCommand.new(new_resource.certfile, keychain)

execute 'unlock keychain' do
command [*test.unlock_keychain('vagrant')]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's change 'vagrant' to node['macos']['admin_user'] attribute; just in case someones uses a different username in their environment.

Copy link
Member Author

Choose a reason for hiding this comment

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

done

end

action :install do
test = SecurityCommand.new(new_resource.certfile, keychain)
Copy link
Collaborator

@americanhanko americanhanko Mar 1, 2018

Choose a reason for hiding this comment

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

I think we can use a better name than test here: perhaps certificate or resource_cert?

Copy link
Member Author

Choose a reason for hiding this comment

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

done

end

def install_certificate(cert_passwd)
if ::File.extname(@cert) == '.p12'
Copy link
Collaborator

Choose a reason for hiding this comment

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

In the X.509 standard, there are a few different file extensions used for the various types of files. For certificates, we might find them with .der, .crt, or .cer and for PKCS #12 files, we might find them with .p12 of pfx.

Might it make sense to check for these possible variations? Could we test for that?

Copy link
Member Author

Choose a reason for hiding this comment

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

The 'import' command can actually support the formats: openssl, bsafe, raw, pkcs7, pkcs8, pkcs12, x509, openssh1, openssh2, and pemseq. We can probably make an array of acceptable file formats and check against those.

elsif ::File.extname(@cert) == '.cer'
add_certificates
else
Chef::Exception.fatal('invalid cert file')
Copy link
Collaborator

Choose a reason for hiding this comment

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

In the interest of informative exceptions, let's include the certificate in the exception message. Maybe something like this?

Chef::Exception.fatal("Invalid certificate: #{@cert}. We looked in the #{@keychain} keychain. Double-check the integrity of both the certificate and the keychain.")

Copy link
Member Author

Choose a reason for hiding this comment

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

done

@@ -0,0 +1,28 @@
resource_name :certificate
default_action :install
Copy link
Collaborator

Choose a reason for hiding this comment

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

If there is only one action, we don't need to indicate a default. If we decide to add more actions later, then we'll need to add it. I would probably remove it until then, but it's up to you.

Copy link
Member Author

Choose a reason for hiding this comment

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

done

describe MacOS::SecurityCommand do
context 'when SecurityCommand object is instantiated' do
it 'security should use /usr/bin/security' do
test = MacOS::SecurityCommand.new('/Users/vagrant/Test.p12', '')
Copy link
Collaborator

@americanhanko americanhanko Mar 1, 2018

Choose a reason for hiding this comment

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

We don't need to instantiate this object for every test. Let's use let to create these objects, as well as some more descriptive names for the ones that get used:

describe MacOS::SecurityCommand do
  let(:p12_cert) { MacOS::SecurityCommand.new('/Users/vagrant/Test.p12', '') }
  
  context 'when a SecurityCommand object is instantiated' do
    it 'security should use /usr/bin/security' do
      expect(p12_cert.security).to eq '/usr/bin/security'
    end
  end
end

From betterspecs.org:

When you have to assign a variable instead of using a before block to create an instance variable, use let. Using let the variable lazy loads only when it is used the first time in the test and get cached until that specific test is finished.

Copy link
Member Author

Choose a reason for hiding this comment

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

done

class SecurityCommand
attr_reader :cert
attr_reader :keychain
attr_reader :security
Copy link
Collaborator

Choose a reason for hiding this comment

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

If this attribute is the path to the command, let's indicate that with the name. Something like :security_executable, :security_command or :security_cmd

Copy link
Member Author

Choose a reason for hiding this comment

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

done

- Use ternary statements
- Change vagrant password
- Rename created certificate object for testing
- Support more file types by putting them in an array before calling add
  or import
- Use more informative exception
Copy link
Contributor

@jazaval jazaval left a comment

Choose a reason for hiding this comment

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

Great work here @mjmerin - thanks to @americanhanko as well for the good feedback.

I'm excited to see what this looks like when paired with a keychain resource!

@jazaval jazaval merged commit d1fc2ed into release/1.7 Mar 5, 2018
@americanhanko americanhanko deleted the feature/pre_cert_rsrc branch March 7, 2018 03:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants