#! /usr/bin/env ruby # # hiki-passwd-collector -- hiki CGI password fields generator # SCRIPTNAME = 'hiki-passwd-collector.rb' SYNOPTIC = "#{SCRIPTNAME} は各ユーザのホームディレクトリの ~/.hiki-passwd ファイルを読み込み, hiki/data/hiki.conf 内のパスワードを更新 するためのスクリプトである. " USAGE = "#{SCRIPTNAME} [OPTIONS] -o hiki.conf" MAINTAINERS = ['Yasuhiro MORIKAWA ', ] UPDATE = '2007/11/15' VER = '0.2' URL = 'http://epa.scitec.kobe-u.ac.jp/~itpass' # 履歴: # Ver. 0.2, 2007/11/15 森川靖大 # - グループに属するユーザのみの ~/.hiki-passwd をチェックできるよう # 機能追加. # # Ver. 0.1, 2007/11/12 森川靖大 # - ともあれ作成 # # TODO: # ############################################################ # Settings # 探査するパスワードファイル (ホームディレクトリからの相対パス) PASSWD_FILE = '.hiki-passwd' # バックアップファイルのサフィックス BACKUP_SUFFIX = '.bk' # 探査する UID の最小値と最大値 UID_MIN = 1000 UID_MAX = 29999 # テンポラリファイル TMPDIR = "/tmp/" TMPFILE = File.basename(__FILE__, '.rb') # End Settings ############################################################ require 'etc' require 'optparse' require 'tempfile' # オプション解析 (banner, width, indent) opt = OptionParser.new('', 20, ' ' * 6) OPTS = Hash.new opt.on('-o', '--output FILE', 'hiki.conf ファイル (必須)' ) { |v| OPTS[:output] = v } opt.on('-g', '--group GROUP', 'グループ名またはグループID. このグループに所属する ユーザのパスワードファイルを探査. デフォルトはプログラムを起動したグループ. ' ) { |v| OPTS[:group] = v } opt.on('-a', '--all', '全ての個人ユーザのパスワードファイルを探査. --group オプションは上書きされる.' ) { OPTS[:all] = true } opt.on('-b', '--backup', 'バックアップファイルの保存' ) { OPTS[:backup] = true } opt.on('-q', '--quiet', 'メッセージを表示しない. --dryrun, --debug オプション が有効な場合には, このオプションは無効となる' ) { OPTS[:quiet] = true } opt.on('-n', '--dryrun', 'どのような動作をするかだけ表示し, 実際には何もしない' ) { OPTS[:dryrun] = true } opt.on('-D', '--debug', 'デバッグ用メッセージを出力 (開発者用).' ) { OPTS[:debug] = true } opt.on('-h', '-H', '--help', 'この, ヘルプメッセージを出力.' ) { OPTS[:help] = true } opt.parse!(ARGV) rescue OPTS[:error] = $!.to_s # [-h, -H, --help] が指定された場合はヘルプを出力して終了 if OPTS[:help] HELP = " #{SCRIPTNAME} SYNOPTIC: #{SYNOPTIC} USAGE: #{USAGE} OPTION: #{opt.help} EXAMPLE: #{SCRIPTNAME} -o ~/hiki/data/hiki.conf #{SCRIPTNAME} -b -q -o ~/hiki/data/hiki.conf -g www-data #{SCRIPTNAME} -b -D -o ~/hiki/data/hiki.conf -a #{SCRIPTNAME} -n -o ~/hiki/data/hiki.conf VERSION: #{SCRIPTNAME} Version #{VER}, Last Update: #{UPDATE}. MAINTAINERS: " print(HELP) MAINTAINERS.each { |v| print "%6s" % ' ', v, "\n" } print "%8s" % ' ', "All Right Reserved.\n\n" exit 0 # [-o, --output] が指定されない場合やオプション解析のエラー時は USAGE を出力して終了. elsif !OPTS[:output] || OPTS[:output] == '' || OPTS[:error] STDERR.print "#{OPTS[:error]}\n\n" if OPTS[:error] warn "#{USAGE}\n\n\"#{SCRIPTNAME} --help\" でヘルプを表示します" exit 1 end if OPTS[:dryrun] print "\n Note: now dryrun mode.\n\n" end persons_info = [] if OPTS[:all] print " \"--all\" option is specified. All user password files are searched.\n" if OPTS[:debug] Etc.passwd { |pwd| if (pwd.uid >= UID_MIN && pwd.uid <= UID_MAX && Etc.getgrgid(pwd.uid).mem.size < 1 ) persons_info << {'uid' => pwd.uid, 'name' => pwd.name, 'dir' => pwd.dir } end } else if !(OPTS[:group]) || OPTS[:group].empty? gid = Process.gid gpwd = Etc.getgrgid(gid) elsif OPTS[:group] =~ /[0-9]+/ gid = OPTS[:group].to_i gpwd = Etc.getgrgid(gid) else gpwd = Etc.getgrnam(OPTS[:group]) gid = gpwd.gid end print " Group ID is #{gid}.\n" if OPTS[:debug] print " Group members are #{gpwd.mem.join(',')}.\n" if OPTS[:debug] Etc.passwd { |pwd| if ( gpwd.mem.include?( pwd.name ) ) persons_info << {'uid' => pwd.uid, 'name' => pwd.name, 'dir' => pwd.dir } end } end print " Personal user list:\n" if OPTS[:debug] persons_info.each{ |info| print " "; p info } if OPTS[:debug] passwd_list = [] print " Loading files:\n" if OPTS[:debug] persons_info.each{ |info| filename = File.join(info['dir'], PASSWD_FILE) if File.readable?( filename ) print " \"#{filename}\"\n" if OPTS[:debug] open(filename) {|fileio| while l = fileio.gets name, passwd = l.chomp.split(':') if name == info['name'] passwd_list << {'name' => name, 'passwd' => passwd} end end } end } print " Password information from \"#{PASSWD_FILE}\":\n" if OPTS[:debug] passwd_list.each{ |pwd| print " "; p pwd } if OPTS[:debug] pre_passwd_field = '' post_passwd_field = '' old_passwd_list = [] open(OPTS[:output]) {|fileio| scope_start = false scope_end = false while l = fileio.gets if scope_end post_passwd_field << l elsif scope_start if l =~ /\}/ scope_end = true post_passwd_field << l next end fields = l.split('=>') name = fields[0].gsub(/[ \"\']*/, '') passwd = fields[1].gsub(/[ \"\', \n]*/, '') old_passwd_list << {'name' => name, 'passwd' => passwd, 'override' => false} elsif l =~ /[\"\']user\.list[\"\'] *\=\> *\{/ scope_start = true pre_passwd_field << l else pre_passwd_field << l end end } print " Password information from \"#{OPTS[:output]}\":\n" if OPTS[:debug] old_passwd_list.each{ |pwd| print " "; p pwd } if OPTS[:debug] new_passwd_list = [] passwd_list.each { |pwd| override = false same = false old_passwd_list.collect!{ |opwd| if pwd['name'] == opwd['name'] if pwd['passwd'] == opwd['passwd'] same = true else opwd['override'] = true override = true end end opwd } unless same if override print " Override: password of \"#{pwd['name']}\"\n" if OPTS[:dryrun] || OPTS[:debug] else print " Add: password of \"#{pwd['name']}\"\n" if OPTS[:dryrun] || OPTS[:debug] end new_passwd_list << pwd end } if new_passwd_list.size < 1 print " New password information is not found.\n" if !(OPTS[:quiet]) print " \"#{OPTS[:output]}\" is not changed.\n\n" if !(OPTS[:quiet]) exit 0 end old_passwd_list.each{ |opwd| if !(opwd['override']) new_passwd_list << opwd end } passwd_field = "" new_passwd_list.each{ |npwd| passwd_field << "\"#{npwd['name']}\" => \"#{npwd['passwd']}\",\n" } #temp = open(File.join(TMPDIR, TMPFILE), "w") temp = Tempfile::new(TMPFILE, TMPDIR) if !(OPTS[:dryrun]) temp.puts(pre_passwd_field + passwd_field + post_passwd_field) if !(OPTS[:dryrun]) temp.close if !(OPTS[:dryrun]) if OPTS[:backup] bkfile = OPTS[:output] + BACKUP_SUFFIX File.rename(OPTS[:output], bkfile) if !(OPTS[:dryrun]) print " Original file has been moved to \"#{bkfile}\".\n" if !(OPTS[:quiet]) end temp.open if !(OPTS[:dryrun]) open(OPTS[:output], "w") {|f| temp.each {|line| f.puts(line) }} if !(OPTS[:dryrun]) print " \"#{OPTS[:output]}\" is updated.\n" if !(OPTS[:quiet]) #temp.close temp.close(true) if !(OPTS[:dryrun])