require "win32ole" require "Win32API" require "socket" module Sys class AdminError < StandardError; end class Group attr_accessor :caption, :description, :domain, :install_date attr_accessor :name, :sid, :status, :gid attr_writer :local def initialize yield self if block_given? end def local? @local end def sid_type @sid_type end def sid_type=(stype) case stype when 1 @sid_type = "user" when 2 @sid_type = "group" when 3 @sid_type = "domain" when 4 @sid_type = "alias" when 5 @sid_type = "well_known_group" when 6 @sid_type = "deleted_account" when 7 @sid_type = "invalid" when 8 @sid_type = "unknown" when 9 @sid_type = "computer" else @sid_type = "unknown" end @sid_type end end class User attr_accessor :caption, :description, :domain, :password attr_accessor :full_name, :install_date, :name, :sid, :status attr_writer :disabled, :local, :lockout, :password_changeable attr_writer :password_expires, :password_required attr_reader :account_type def initialize yield self if block_given? end def account_type=(type) case type when 256 @account_type = "duplicate" when 512 @account_type = "normal" when 2048 @account_type = "interdomain_trust" when 4096 @account_type = "workstation_trust" when 8192 @account_type = "server_trust" else @account_type = "unknown" end end def sid_type @sid_type end def sid_type=(stype) case stype when 1 @sid_type = "user" when 2 @sid_type = "group" when 3 @sid_type = "domain" when 4 @sid_type = "alias" when 5 @sid_type = "well_known_group" when 6 @sid_type = "deleted_account" when 7 @sid_type = "invalid" when 8 @sid_type = "unknown" when 9 @sid_type = "computer" else @sid_type = "unknown" end end def disabled? @disabled end def local? @local end def lockout? @lockout end def password_changeable? @password_changeable end def password_expires? @password_expires end def password_required? @password_required end end class Admin VERSION = "1.3.1" # Deletes +userid+ from the given +host+, or the local host if no host # is specified. # def self.delete_user(userid=nil, host=Socket.gethostname) begin adsi = WIN32OLE.connect("WinNT://#{host},Computer") rescue WIN32OLERuntimeError => err raise AdminError, err end begin adsi.delete("user", userid) rescue WIN32OLERuntimeError => err raise AdminError, err end end # Returns the user name (only) of the current login. # def self.get_login getlogin = Win32API.new("advapi32","GetUserName",['P','P'],'L') buffer = "\0" * 256; nsize = [256].pack("L") getlogin.call(buffer,nsize) len = nsize.unpack("L")[0] username = buffer[0 ... len].chop username end # Returns a User object based on either +name+ or +uid+. # # call-seq: # get_user(name, host=localhost) # get_user(uid, host=localhost, local=true) # # You may specify a +host+ from which information is retrieved. The # default is the local machine. You may also specify whether to # retrieve a local or global account. The default is local. # def self.get_user(uid, host=Socket.gethostname, local=true) host = Socket.gethostname if host.nil? cs = "winmgmts:{impersonationLevel=impersonate}!" cs << "//#{host}/root/cimv2" begin wmi = WIN32OLE.connect(cs) rescue WIN32OLERuntimeError => e raise AdminError, e end query = "select * from win32_useraccount" query << " where localaccount = true" if local if uid.kind_of?(Fixnum) if local query << " and sid like '%-#{uid}'" else query << " where sid like '%-#{uid}'" end else if local query << " and name = '#{uid}'" else query << " where name = '#{uid}'" end end wmi.execquery(query).each{ |user| # Because our 'like' query isn't fulproof, let's parse # the SID again to make sure if uid.kind_of?(Fixnum) if user.sid.split("-").last.to_i != uid next end end usr = User.new do |u| u.account_type = user.accounttype u.caption = user.caption u.description = user.description u.disabled = user.disabled u.domain = user.domain u.full_name = user.fullname u.install_date = user.installdate u.local = user.localaccount u.lockout = user.lockout u.name = user.name u.password_changeable = user.passwordchangeable u.password_expires = user.passwordexpires u.password_required = user.passwordrequired u.sid = user.sid u.sid_type = user.sidtype u.status = user.status end return usr } end # In block form, yields a User object for each user on the system. In # non-block form, returns an Array of User objects. # # call-seq: # users(host=localhost, local=true) # users(host=localhost, local=true){ |user| ... } # # You may specify a host from which information is retrieved. The # default is the local machine. You can retrieve either a global or # group, depending on the value of the +local+ argument. # def self.users(host=Socket.gethostname, local=true) host = Socket.gethostname if host.nil? cs = "winmgmts:{impersonationLevel=impersonate}!" cs << "//#{host}/root/cimv2" begin wmi = WIN32OLE.connect(cs) rescue WIN32OLERuntimeError => e raise AdminError, e end query = "select * from win32_useraccount" query << " where localaccount = true" if local array = [] wmi.execquery(query).each{ |user| usr = User.new do |u| u.account_type = user.accounttype u.caption = user.caption u.description = user.description u.disabled = user.disabled u.domain = user.domain u.full_name = user.fullname u.install_date = user.installdate u.local = user.localaccount u.lockout = user.lockout u.name = user.name u.password_changeable = user.passwordchangeable u.password_expires = user.passwordexpires u.password_required = user.passwordrequired u.sid = user.sid u.sid_type = user.sidtype u.status = user.status end if block_given? yield usr else array.push(usr) end } return array unless block_given? end # Returns a Group object based on either +name+ or +uid+. # # call-seq: # get_group(name, host=localhost, local=true) # get_group(gid, host=localhost, local=true) # # You may specify a host from which information is retrieved. # The default is the local machine. You can retrieve either a global or # local group, depending on the value of the +local+ argument. # def self.get_group(grp, host=Socket.gethostname, local=true) host = Socket.gethostname if host.nil? cs = "winmgmts:{impersonationLevel=impersonate}!" cs << "//#{host}/root/cimv2" gid = nil begin wmi = WIN32OLE.connect(cs) rescue WIN32OLERuntimeError => e raise AdminError, e end query = "select * from win32_group" query << " where localaccount = true" if local if grp.kind_of?(Fixnum) if local query << " and sid like '%-#{grp}'" else query << " where sid like '%-#{grp}'" end else if local query << " and name = '#{grp}'" else query << " where name = '#{grp}'" end end wmi.execquery(query).each{ |group| gid = group.sid.split("-").last.to_i # Because our 'like' query isn't fulproof, let's parse # the SID again to make sure if grp.kind_of?(Fixnum) next if grp != gid end grp = Group.new do |g| g.caption = group.caption g.description = group.description g.domain = group.domain g.gid = gid g.install_date = group.installdate g.local = group.localaccount g.name = group.name g.sid = group.sid g.sid_type = group.sidtype g.status = group.status end return grp } # If we're here, it means it wasn't found. raise AdminError, "no group found for '#{grp}'" end # In block form, yields a Group object for each user on the system. In # non-block form, returns an Array of Group objects. # # call-seq: # groups(host=localhost, local=true) # groups(host=localhost, local=true){ |group| ... } # # You may specify a host from which information is retrieved. # The default is the local machine. You can retrieve either a global or # local group, depending on the value of the +local+ argument. # def self.groups(host=Socket.gethostname, local=true) host = Socket.gethostname if host.nil? cs = "winmgmts:{impersonationLevel=impersonate}!" cs << "//#{host}/root/cimv2" begin wmi = WIN32OLE.connect(cs) rescue WIN32OLERuntimeError => e raise AdminError, e end query = "select * from win32_group" query << " where localaccount = true" if local array = [] wmi.execquery(query).each{ |group| grp = Group.new do |g| g.caption = group.caption g.description = group.description g.domain = group.domain g.gid = group.sid.split("-").last.to_i g.install_date = group.installdate g.local = group.localaccount g.name = group.name g.sid = group.sid g.sid_type = group.sidtype g.status = group.status end if block_given? yield grp else array.push(grp) end } return array unless block_given? end end end