Permalink
Browse files

add the htpasswd cookbook

  • Loading branch information...
1 parent 0d7f23c commit 95a94dfee3d6ac4e7b446036f232c819e595060c @phunehehe committed Dec 19, 2012
View
@@ -0,0 +1,37 @@
+Description
+===========
+
+Manage an htpasswd file.
+If htpasswd exe isn't found, we install a python implementation.
+
+Requirements
+============
+
+Work on linux with python for build-in implementation
+
+Resource/Provider
+=================
+
+This cookbook includes LWRPs for managing:
+* htpasswd
+
+htpasswd
+--------
+
+# Actions
+
+- :add: Add a login in a htpasswd file. modify it if there is already one and create file if not found (default)
+- :overwrite: Overwrite an htpasswd file (delete file and add user).
+
+# Attribute Parameters
+
+- package_name: file attribute. path of the htpaswwd to manage
+- user: user to create
+- password: password for the user
+
+# Example
+ # add user "foo" with password "bar" to "/etc/nginx/htpassword"
+ htpasswd "/etc/nginx/htpassword" do
+ user "foo"
+ password "bar"
+ end
@@ -0,0 +1 @@
+default[:htpasswd][:install_dir] = "/usr/local/bin"
@@ -0,0 +1,123 @@
+#!/usr/bin/python
+"""Replacement for htpasswd"""
+# Original author: Eli Carter
+
+import os
+import sys
+import random
+from optparse import OptionParser
+
+# We need a crypt module, but Windows doesn't have one by default. Try to find
+# one, and tell the user if we can't.
+try:
+ import crypt
+except ImportError:
+ try:
+ import fcrypt as crypt
+ except ImportError:
+ sys.stderr.write("Cannot find a crypt module. "
+ "Possibly http://carey.geek.nz/code/python-fcrypt/\n")
+ sys.exit(1)
+
+
+def salt():
+ """Returns a string of 2 randome letters"""
+ letters = 'abcdefghijklmnopqrstuvwxyz' \
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' \
+ '0123456789/.'
+ return random.choice(letters) + random.choice(letters)
+
+
+class HtpasswdFile:
+ """A class for manipulating htpasswd files."""
+
+ def __init__(self, filename, create=False):
+ self.entries = []
+ self.filename = filename
+ if not create:
+ if os.path.exists(self.filename):
+ self.load()
+ else:
+ raise Exception("%s does not exist" % self.filename)
+
+ def load(self):
+ """Read the htpasswd file into memory."""
+ lines = open(self.filename, 'r').readlines()
+ self.entries = []
+ for line in lines:
+ username, pwhash = line.split(':')
+ entry = [username, pwhash.rstrip()]
+ self.entries.append(entry)
+
+ def save(self):
+ """Write the htpasswd file to disk"""
+ open(self.filename, 'w').writelines(["%s:%s\n" % (entry[0], entry[1])
+ for entry in self.entries])
+
+ def update(self, username, password):
+ """Replace the entry for the given user, or add it if new."""
+ pwhash = crypt.crypt(password, salt())
+ matching_entries = [entry for entry in self.entries
+ if entry[0] == username]
+ if matching_entries:
+ matching_entries[0][1] = pwhash
+ else:
+ self.entries.append([username, pwhash])
+
+ def delete(self, username):
+ """Remove the entry for the given user."""
+ self.entries = [entry for entry in self.entries
+ if entry[0] != username]
+
+
+def main():
+ """%prog [-c] -b filename username password
+ Create or update an htpasswd file"""
+ # For now, we only care about the use cases that affect tests/functional.py
+ parser = OptionParser(usage=main.__doc__)
+ parser.add_option('-b', action='store_true', dest='batch', default=False,
+ help='Batch mode; password is passed on the command line IN THE CLEAR.'
+ )
+ parser.add_option('-c', action='store_true', dest='create', default=False,
+ help='Create a new htpasswd file, overwriting any existing file.')
+ parser.add_option('-D', action='store_true', dest='delete_user',
+ default=False, help='Remove the given user from the password file.')
+
+ options, args = parser.parse_args()
+
+ def syntax_error(msg):
+ """Utility function for displaying fatal error messages with usage
+ help.
+ """
+ sys.stderr.write("Syntax error: " + msg)
+ sys.stderr.write(parser.get_usage())
+ sys.exit(1)
+
+ if not options.batch:
+ syntax_error("Only batch mode is supported\n")
+
+ # Non-option arguments
+ if len(args) < 2:
+ syntax_error("Insufficient number of arguments.\n")
+ filename, username = args[:2]
+ if options.delete_user:
+ if len(args) != 2:
+ syntax_error("Incorrect number of arguments.\n")
+ password = None
+ else:
+ if len(args) != 3:
+ syntax_error("Incorrect number of arguments.\n")
+ password = args[2]
+
+ passwdfile = HtpasswdFile(filename, create=options.create)
+
+ if options.delete_user:
+ passwdfile.delete(username)
+ else:
+ passwdfile.update(username, password)
+
+ passwdfile.save()
+
+
+if __name__ == '__main__':
+ main()
View
@@ -0,0 +1,35 @@
+{
+ "name": "htpasswd",
+ "description": "Provider for htpasswd",
+ "long_description": "Description\n===========\n\nManage an htpasswd file.\nIf htpasswd exe isn't found, we install a python implementation.\n\nRequirements\n============\n\nWork on linux with python for build-in implementation\n\nResource/Provider\n=================\n\nThis cookbook includes LWRPs for managing:\n* htpasswd\n\nhtpasswd\n--------\n\n# Actions\n\n- :add: Add a login in a htpasswd file. modify it if there is already one and create file if not found (default)\n- :overwrite: Overwrite an htpasswd file (delete file and add user).\n\n# Attribute Parameters\n\n- package_name: file attribute. path of the htpaswwd to manage\n- user: user to create\n- password: password for the user\n\n# Example\n # add user \"foo\" with password \"bar\" to \"/etc/nginx/htpassword\"\n htpasswd \"/etc/nginx/htpassword\" do\n user \"foo\"\n password \"bar\"\n end\n",
+ "maintainer": "Guilhem Lettron",
+ "maintainer_email": "guilhem.lettron@youscribe.com",
+ "license": "Apache 2.0",
+ "platforms": {
+ "ubuntu": ">= 0.0.0",
+ "debian": ">= 0.0.0",
+ "centos": ">= 0.0.0",
+ "redhat": ">= 0.0.0",
+ "fedora": ">= 0.0.0"
+ },
+ "dependencies": {
+ },
+ "recommendations": {
+ },
+ "suggestions": {
+ "python": ">= 0.0.0"
+ },
+ "conflicting": {
+ },
+ "providing": {
+ },
+ "replacing": {
+ },
+ "attributes": {
+ },
+ "groupings": {
+ },
+ "recipes": {
+ },
+ "version": "0.0.1"
+}
View
@@ -0,0 +1,11 @@
+maintainer "Guilhem Lettron"
+maintainer_email "guilhem.lettron@youscribe.com"
+license "Apache 2.0"
+description "Provider for htpasswd"
+long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
+version "0.0.1"
+suggests "python"
+
+%w{ ubuntu debian centos redhat fedora }.each do |os|
+ supports os
+end
@@ -0,0 +1,50 @@
+#
+# Cookbook Name:: htpasswd
+# Provider:: htpasswd
+# Author:: Guilhem Lettron <guilhem.lettron@youscribe.com>
+#
+# Copyright 20012, Societe Publica.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+def initialize(*args)
+ super
+ @action = :add
+end
+
+action :add do
+ if ::File.exists?(new_resource.file)
+ add
+ else
+ create
+ end
+end
+
+action :overwrite do
+ create
+end
+
+private
+
+def create
+ execute "create htpasswd" do
+ command "htpasswd -c -b #{new_resource.file} #{new_resource.user} #{new_resource.password}"
+ end
+end
+
+def add
+ execute "add to htpasswd" do
+ command "htpasswd -b #{new_resource.file} #{new_resource.user} #{new_resource.password}"
+ end
+end
@@ -0,0 +1,26 @@
+#
+# Cookbook Name:: htpasswd
+# recipe:: build-in
+# Author:: Guilhem Lettron <guilhem.lettron@youscribe.com>
+#
+# Copyright 2012, Societe Publica.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+cookbook_file "#{node[:htpasswd][:install_dir]}/htpasswd" do
+ source "htpasswd.py"
+ mode "0755"
+ owner "root"
+ group "root"
+end
@@ -0,0 +1,34 @@
+#
+# Cookbook Name:: htpasswd
+# recipe:: default
+# Author:: Guilhem Lettron <guilhem.lettron@youscribe.com>
+#
+# Copyright 2012, Societe Publica.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+def which(cmd)
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
+ exts.each { |ext|
+ exe = "#{path}/#{cmd}#{ext}"
+ return exe if File.executable? exe
+ }
+ end
+ return nil
+end
+
+unless which("htpasswd")
+ include_recipe "htpasswd::build-in"
+end
@@ -0,0 +1,10 @@
+actions :add, :overwrite
+
+attribute :file, :kind_of => String, :name_attribute => true
+attribute :user, :kind_of => String, :required => true
+attribute :password, :kind_of => String, :required => true
+
+def initialize(*args)
+ super
+ @action = :add
+end

0 comments on commit 95a94df

Please sign in to comment.