ucert 0.2.57
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +134 -0
- data/LICENSE.txt +22 -0
- data/README.rdoc +61 -0
- data/Rakefile +8 -0
- data/TODO +5 -0
- data/bin/ad_dump +74 -0
- data/bin/ad_update +48 -0
- data/bin/adgrep +149 -0
- data/bin/adp_dump +70 -0
- data/bin/aix_dump +69 -0
- data/bin/audiolog_dump +69 -0
- data/bin/bloomberg_dump +69 -0
- data/bin/check21_dump +69 -0
- data/bin/citidirect_be_dump +69 -0
- data/bin/citidirect_sec_dump +69 -0
- data/bin/citrixsf_dump +70 -0
- data/bin/clear_par_dump +70 -0
- data/bin/cmbrun_ny_dump +76 -0
- data/bin/cvm_dump +70 -0
- data/bin/db_direct_dump +69 -0
- data/bin/egifts_dump +68 -0
- data/bin/equinix_dump +69 -0
- data/bin/frb_dump +69 -0
- data/bin/go_contact_dump +16 -0
- data/bin/jpm_dump +69 -0
- data/bin/madison535_dump +70 -0
- data/bin/mantis_dump +79 -0
- data/bin/prime_dump +79 -0
- data/bin/sage100_dump +69 -0
- data/bin/sharefile_dump +69 -0
- data/bin/som_dump +74 -0
- data/bin/stb_dump +69 -0
- data/bin/swift_dump +79 -0
- data/bin/swift_online_dump +69 -0
- data/bin/t24_dump +79 -0
- data/bin/vpn_dump +69 -0
- data/bin/wms_dump +79 -0
- data/bin/yst_dump +79 -0
- data/data/ad/ad_delta.txt +94 -0
- data/data/ad/hosts +421 -0
- data/data/ad/hosts.old +597 -0
- data/data/ad/hosts_old +597 -0
- data/data/ad/ldap_computer.txt +19028 -0
- data/data/ad/ldap_person.txt +41241 -0
- data/data/adp/Active Employee Report.xlsx +0 -0
- data/data/adp/adp_user_map.txt +141 -0
- data/data/aix/EGIFTS1.txt +239 -0
- data/data/aix/NYSWIFT1.txt +222 -0
- data/data/aix/T24_APP1.txt +300 -0
- data/data/aix/T24_DBP.txt +252 -0
- data/data/aix/aix_user_map.txt +46 -0
- data/data/alliance_swift/Swift_Operator_Details.xlsx +0 -0
- data/data/alliance_swift/Swift_Operator_Profiles_Details.xlsx +0 -0
- data/data/alliance_swift/swift_operator_map.txt +22 -0
- data/data/audiolog/Capture_audiolog.PNG +0 -0
- data/data/bloomberg/AccountData.csv +2 -0
- data/data/bloomberg/Capture_SID_download.PNG +0 -0
- data/data/bloomberg/current_subscriptions.csv +11 -0
- data/data/check21/Capture_check21_users.PNG +0 -0
- data/data/citidirect_be/Capture.PNG +0 -0
- data/data/citidirect_be/Capture_new.PNG +0 -0
- data/data/citidirect_be/Capture_new_new.PNG +0 -0
- data/data/citidirect_be/UserProfileEntitlementReport.pdf +0 -0
- data/data/citidirect_be/UserProfileEntitlementsReport.old.xlsx +0 -0
- data/data/citidirect_be/UserProfileEntitlementsReport.xlsx +0 -0
- data/data/citidirect_be/be_user_map.txt +11 -0
- data/data/citidirect_securities/Capture.PNG +0 -0
- data/data/citidirect_securities/User_Entitlements_Report___CLNT.dat +19 -0
- data/data/citidirect_securities/User_Entitlements_Report___CLNT.xml +75 -0
- data/data/citidirect_securities/citidirect_securities_user_map.txt +10 -0
- data/data/citrix_sharefile/ShareFile_Access_Report.xlsx +0 -0
- data/data/citrix_sharefile/sharefile_user_map.txt +33 -0
- data/data/clear_par/ClearPar User Report.xlsx +0 -0
- data/data/clear_par/clear_par_user_map.txt +25 -0
- data/data/cmbrun_ny/CMBNY_Position_Rpt.xlsx +0 -0
- data/data/cmbrun_ny/CMBRUN_USER_RPT.xlsx +0 -0
- data/data/cmbrun_ny/Capture_cmbrun.PNG +0 -0
- data/data/cmbrun_ny/Capture_cmbrun_position.PNG +0 -0
- data/data/cmbrun_ny/crny_access_user_map.txt +55 -0
- data/data/cvm/cvm_user_func.xlsx +0 -0
- data/data/cvm/cvm_user_list.xlsx +0 -0
- data/data/cvm/cvm_user_map.txt +56 -0
- data/data/cvm/cvm_user_role.xlsx +0 -0
- data/data/db_direct/Capture_main.PNG +0 -0
- data/data/db_direct/Capture_rpt.PNG +0 -0
- data/data/db_direct/accountpermission.xlsx +0 -0
- data/data/db_direct/db_direct_user_map.txt +8 -0
- data/data/db_direct/di_direct_user_map.txt +0 -0
- data/data/db_direct/userfulldetail_2016010813232300644912.pdf +0 -0
- data/data/equinix/Secured Access List_CHINA MERCHANTS BANK.xlsx +0 -0
- data/data/equinix/equinix_user_map.txt +29 -0
- data/data/fis_egifts/CHINA_MERCHANTS_BANK_-_USER_ACCOUNT_FUNCTION_REPORT.xlsx +0 -0
- data/data/fis_egifts/egifts_user_map.txt +113 -0
- data/data/fis_prime/Prime_Operator_Rights_Report.xml +41958 -0
- data/data/fis_prime/Prime_Operator_Status_Report.xml +1827 -0
- data/data/fis_prime/Prime_Operators_and_Roles_Report.xml +1505 -0
- data/data/fis_prime/Prime_Rights_by_Role_Report.xml +22726 -0
- data/data/fis_prime/prime_user_map.txt +77 -0
- data/data/frb/FRB_Subscriber_Roles_Report.xlsx +0 -0
- data/data/frb/frb_user_map.txt +22 -0
- data/data/go_contact/go_contact.xlsx +0 -0
- data/data/go_contact/go_user_map.txt +134 -0
- data/data/go_contact/title_level_map.txt +141 -0
- data/data/jpm_access/Capture.PNG +0 -0
- data/data/jpm_access/jpm_access_user_map.txt +13 -0
- data/data/jpm_access/jpm_user_entitlements_details.txt +194 -0
- data/data/jpm_access/jpm_user_groupentitlements_details.txt +2 -0
- data/data/madison535/535madison_bldg_pass.xlsx +0 -0
- data/data/madison535/535madison_bldg_pass_2.xlsx +0 -0
- data/data/madison535/madison535_user_map.txt +191 -0
- data/data/mantis/Mantis_AccessLevels.xlsx +0 -0
- data/data/mantis/Mantis_ActiveUsers_Rpt.xlsx +0 -0
- data/data/mantis/mantis_access_user_map.txt +128 -0
- data/data/sage100/Capture_Sage100_Rpt.PNG +0 -0
- data/data/sage100/Capture_Sage100_Rpt_2.PNG +0 -0
- data/data/sage100/SY_UserReport_RolePreferencesDetails.xlsx +0 -0
- data/data/sage100/SY_UserReport_RoleTaskPermissionsDetails.xlsx +0 -0
- data/data/sage100/sy_user_map.txt +14 -0
- data/data/som/som_user_map.txt +40 -0
- data/data/som/som_user_report.csv +329 -0
- data/data/stb/STB_USERS.csv +177 -0
- data/data/stb/STB_USERS.pdf +0 -0
- data/data/stb/stb_user_map.txt +33 -0
- data/data/swift_online/UserReport.xlsx +0 -0
- data/data/swift_online/swo_access_user_map.txt +18 -0
- data/data/t24/T24_Grp_Rpt.csv +484 -0
- data/data/t24/T24_User_Rpt.csv +567 -0
- data/data/t24/t24_grp.xml +2904 -0
- data/data/t24/t24_user_map.txt +197 -0
- data/data/t24/t24_usr.xml +9628 -0
- data/data/vpn/Capture_VPN.PNG +0 -0
- data/data/wms/role_rpt.txt +451 -0
- data/data/wms/user_rpt.txt +55 -0
- data/data/wms/wms_user_map.txt +55 -0
- data/data/yst/YiShiTong_Org.csv +21 -0
- data/data/yst/YiShiTong_User.csv +163 -0
- data/data/yst/yst_user_map.txt +163 -0
- data/demos/filter_email.rb +19 -0
- data/demos/idm_ad_reload.rb +164 -0
- data/lib/ucert.rb +82 -0
- data/lib/ucert/ad_tracker.rb +694 -0
- data/lib/ucert/adp_payroll_tracker.rb +189 -0
- data/lib/ucert/aix_tracker.rb +175 -0
- data/lib/ucert/alliance_swift_tracker.rb +300 -0
- data/lib/ucert/audiolog_tracker.rb +67 -0
- data/lib/ucert/bloomberg_tracker.rb +96 -0
- data/lib/ucert/check21_tracker.rb +95 -0
- data/lib/ucert/citidirect_be_tracker.rb +418 -0
- data/lib/ucert/citidirect_securities_tracker.rb +230 -0
- data/lib/ucert/citrix_sharefile_tracker.rb +196 -0
- data/lib/ucert/clear_par_tracker.rb +187 -0
- data/lib/ucert/cmbrun_ny_tracker.rb +244 -0
- data/lib/ucert/cvm_tracker.rb +230 -0
- data/lib/ucert/db_direct_tracker.rb +205 -0
- data/lib/ucert/equinix_tracker.rb +202 -0
- data/lib/ucert/fis_egifts_tracker.rb +249 -0
- data/lib/ucert/fis_prime_tracker.rb +391 -0
- data/lib/ucert/frb_tracker.rb +232 -0
- data/lib/ucert/go_contact_tracker.rb +778 -0
- data/lib/ucert/jpm_access_tracker.rb +205 -0
- data/lib/ucert/madison535_tracker.rb +273 -0
- data/lib/ucert/mantis_tracker.rb +249 -0
- data/lib/ucert/sage100_tracker.rb +355 -0
- data/lib/ucert/som_tracker.rb +223 -0
- data/lib/ucert/stb_tracker.rb +199 -0
- data/lib/ucert/swift_online_tracker.rb +197 -0
- data/lib/ucert/t24_tracker.rb +342 -0
- data/lib/ucert/utils/utils.rb +200 -0
- data/lib/ucert/vpn_tracker.rb +94 -0
- data/lib/ucert/wms_tracker.rb +240 -0
- data/lib/ucert/yst_tracker.rb +264 -0
- data/test/ad_testfiles/ldap_computer_test.txt +21 -0
- data/test/ad_testfiles/ldap_person_test.txt +21 -0
- data/test/aix_testfiles/application1.txt +7 -0
- data/test/aix_testfiles/application2.txt +15 -0
- data/test/alliance_swift_testfiles/Swift_Operator_Details_Test.xlsx +0 -0
- data/test/alliance_swift_testfiles/Swift_Operator_Profiles_Details_Test.xlsx +0 -0
- data/test/alliance_swift_testfiles/swift_operator_map_test.txt +55 -0
- data/test/alliance_swift_testfiles/swift_operator_map_test_2.txt +55 -0
- data/test/alliance_swift_testfiles/swift_operator_map_test_format_fixed.txt +55 -0
- data/test/citidirect_be_testfiles/UserProfileEntitlementsReport_Test.xlsx +0 -0
- data/test/citidirect_securities_testfiles/User_Entitlements_Report___CLNT_Test.xml +48 -0
- data/test/citrix_sharefile_testfiles/ShareFile_Access_Report_Test.xlsx +0 -0
- data/test/cmbrun_ny_testfiles/CMBNY_Position_Rpt_02242016_test.xlsx +0 -0
- data/test/cmbrun_ny_testfiles/CMBRUN_USER_RPT_Test.xlsx +0 -0
- data/test/db_direct_testfiles/accountpermission_Test.xlsx +0 -0
- data/test/equinix_testfiles/Secured Access List_CHINA MERCHANTS BANK_TEST.xlsx +0 -0
- data/test/fis_egifts_testfiles/CHINA_MERCHANTS_BANK_-_USER_ACCOUNT_FUNCTION_REPORT_TEST.xlsx +0 -0
- data/test/fis_prime_testfiles/Prime_Operator_Rights_Report_Test.xml +158 -0
- data/test/fis_prime_testfiles/Prime_Operator_Status_Report_Copy.xml +1659 -0
- data/test/fis_prime_testfiles/Prime_Operator_Status_Report_Test.xml +51 -0
- data/test/fis_prime_testfiles/Prime_Operators_and_Roles_Report_Copy.xml +1360 -0
- data/test/fis_prime_testfiles/Prime_Operators_and_Roles_Report_Test.xml +45 -0
- data/test/fis_prime_testfiles/Prime_Rights_by_Role_Report_Test.xml +65 -0
- data/test/fis_prime_testfiles/prime_user_map.txt +3 -0
- data/test/frb_testfiles/FRB_Subscriber_Roles_Report_Test.xlsx +0 -0
- data/test/go_contact_testfiles/go_contact_test.xlsx +0 -0
- data/test/jpm_access_testfiles/Capture.PNG +0 -0
- data/test/jpm_access_testfiles/jpm_user_entitlements_details_original.txt +208 -0
- data/test/jpm_access_testfiles/jpm_user_entitlements_details_test.txt +7 -0
- data/test/madison535_testfiles/535madison_bldg_pass_2_Test.xlsx +0 -0
- data/test/madison535_testfiles/535madison_bldg_pass_Test.xlsx +0 -0
- data/test/mantis_testfiles/Mantis_AccessLevels_Test.xlsx +0 -0
- data/test/mantis_testfiles/Mantis_ActiveUsers_Rpt_Test.xlsx +0 -0
- data/test/sage100_testfiles/SY_UserReport_RolePreferencesDetails_Test.xlsx +0 -0
- data/test/sage100_testfiles/SY_UserReport_RoleTaskPermissionsDetails_Test.xlsx +0 -0
- data/test/som_testfiles/som_user_map_test.txt +7 -0
- data/test/som_testfiles/som_user_report_test.csv +25 -0
- data/test/stb_testfiles/STB_USERS_test.csv +24 -0
- data/test/stb_testfiles/STB_USERS_test_constant.csv +24 -0
- data/test/swift_online_testfiles/UserReport.xls +0 -0
- data/test/swift_online_testfiles/UserReport_Test.xlsx +0 -0
- data/test/swift_online_testfiles/test_outline_level.rb +7 -0
- data/test/t24_testfiles/T24_Grp_Rpt_Test.csv +7 -0
- data/test/t24_testfiles/T24_User_Rpt_Test.csv +7 -0
- data/test/test_ad_tracker.rb +148 -0
- data/test/test_aix_tracker.rb +71 -0
- data/test/test_alliance_swift_tracker.rb +131 -0
- data/test/test_audiolog_tracker.rb +23 -0
- data/test/test_check21_tracker.rb +30 -0
- data/test/test_citidirect_be_tracker.rb +110 -0
- data/test/test_citidirect_securities_tracker.rb +89 -0
- data/test/test_citrix_sharefile_tracker.rb +105 -0
- data/test/test_cmbrun_ny_tracker.rb +112 -0
- data/test/test_db_direct_tracker.rb +125 -0
- data/test/test_equinix_tracker.rb +119 -0
- data/test/test_fis_egifts_tracker.rb +105 -0
- data/test/test_fis_prime_tracker.rb +288 -0
- data/test/test_frb_tracker.rb +104 -0
- data/test/test_go_contact.rb +276 -0
- data/test/test_jpm_access_tracker.rb +122 -0
- data/test/test_madison535_tracker.rb +125 -0
- data/test/test_mantis_tracker.rb +133 -0
- data/test/test_sage100_tracker.rb +120 -0
- data/test/test_som_tracker.rb +71 -0
- data/test/test_stb_tracker.rb +120 -0
- data/test/test_swift_online_tracker.rb +116 -0
- data/test/test_t24_tracker.rb +151 -0
- data/test/test_utils.rb +46 -0
- data/test/test_vpn_tracker.rb +56 -0
- data/test/test_wms_tracker.rb +109 -0
- data/test/test_yst_tracker.rb +133 -0
- data/test/utils_testfiles/file2list_test.txt +13 -0
- data/test/utils_testfiles/load_know_user_map_testfile.txt +4 -0
- data/test/wms_testfiles/role_rpt_test.txt +6 -0
- data/test/wms_testfiles/user_rpt_test.txt +6 -0
- data/test/yst_testfiles/YiShiTong_Org_Test.csv +18 -0
- data/test/yst_testfiles/YiShiTong_User_Test.csv +5 -0
- data/ucert.gemspec +52 -0
- data/version.txt +12 -0
- metadata +410 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
##################################################
|
2
|
+
# Search the AD cache, filter out the DN obj by email first, then filter out the SAM account ID by DN
|
3
|
+
# Usage: ruby filter_email.rb [file_emails]
|
4
|
+
# file_emails contains a list of emails one entry per line
|
5
|
+
|
6
|
+
require "ucert"
|
7
|
+
|
8
|
+
k=Ucert::AdTracker.new(:verbose=>false)
|
9
|
+
File.open(ARGV[0],'r').each do |line|
|
10
|
+
#puts "Processing: #{line}"
|
11
|
+
email=line.chomp.strip
|
12
|
+
my_dn=k.ad_search_by_text(email,"person")
|
13
|
+
my_record=k.get_ad_record(my_dn)
|
14
|
+
my_id=k.get_sam_id(my_dn)
|
15
|
+
my_name=k.get_cn(my_dn)
|
16
|
+
my_department=k.get_department(my_dn)
|
17
|
+
|
18
|
+
puts my_id
|
19
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# ucert
|
4
|
+
#
|
5
|
+
# A Ruby library for enterprise user account certification / recertification / audit project
|
6
|
+
#
|
7
|
+
# Copyright (c) CMBNY Risk Department
|
8
|
+
#++
|
9
|
+
|
10
|
+
# A server side script designed to automatically update the 'cyber_idm' application's 'ad_users'
|
11
|
+
# and 'ad_computers' tables in a scheduled time-table
|
12
|
+
# Usage:
|
13
|
+
# => ruby idm_ad_reload.rb -h
|
14
|
+
|
15
|
+
require 'optparse'
|
16
|
+
require 'ostruct'
|
17
|
+
require "ucert"
|
18
|
+
require "sequel"
|
19
|
+
require "yaml"
|
20
|
+
|
21
|
+
class CmdOptions
|
22
|
+
# Returen an OpenStruct describing the command line options
|
23
|
+
def self.parse(args)
|
24
|
+
@options = OpenStruct.new # OpenStruct is a structure like hash table
|
25
|
+
@options.verbose = false # verbose and banner are key and value
|
26
|
+
@options.banner = "Usage: #{__FILE__} -h"
|
27
|
+
@version = ["0","1"] # Program version information
|
28
|
+
@last_change = "03/14/2016"
|
29
|
+
opt_parser = OptionParser.new do |opts|
|
30
|
+
|
31
|
+
# Boolean switch for the verbose mode switch
|
32
|
+
opts.on("-v", "--[no-]verbose", "Verbose Mode") do |v|
|
33
|
+
@options.verbose = v
|
34
|
+
end
|
35
|
+
|
36
|
+
# Command switch for configuration file
|
37
|
+
opts.on("-lYML", "--ldap=YML", "Ldap YML Configuration File") do |l|
|
38
|
+
@options.ldap = l
|
39
|
+
end
|
40
|
+
|
41
|
+
# Command switch for configuration file
|
42
|
+
opts.on("-dYML", "--db=YML", "Database YML Configuration File") do |d|
|
43
|
+
@options.db = d
|
44
|
+
end
|
45
|
+
|
46
|
+
# Displaying help.
|
47
|
+
opts.on("-h", "--help", "Display Help") do |v|
|
48
|
+
puts opts
|
49
|
+
exit
|
50
|
+
end
|
51
|
+
|
52
|
+
# Another typical switch to print the version.
|
53
|
+
opts.on_tail("-V","--version", String, "Show Program Version") do
|
54
|
+
@options.version = "Program Version: #{@version.join('.')}; Last Modification: #{@last_change}"
|
55
|
+
puts @options.version
|
56
|
+
exit
|
57
|
+
end
|
58
|
+
end
|
59
|
+
opt_parser.parse!(args)
|
60
|
+
puts "Captured Command Line Arguments: #{@options}" if @options.verbose
|
61
|
+
return @options
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
##########################################################################
|
66
|
+
### Main ###
|
67
|
+
##########################################################################
|
68
|
+
# Command line arguments structure
|
69
|
+
@options=CmdOptions.parse(ARGV)
|
70
|
+
|
71
|
+
ldap_yml = @options.ldap || "/home/liyang138/cyber_idm/config/ldap.yml"
|
72
|
+
db_yml = @options.db || "/home/liyang138/cyber_idm/config/database.yml"
|
73
|
+
|
74
|
+
# Step 1 - Read the AD connecting db string from the 'cyber_idm' configuration file 'ldap.yml'
|
75
|
+
ldap_env = YAML.load_file(ldap_yml)[ENV['RAILS_ENV'] || 'development']
|
76
|
+
db_env = YAML.load_file(db_yml)[ENV['RAILS_ENV'] || 'development']
|
77
|
+
|
78
|
+
# Step 2 - Update the local cache database for AD
|
79
|
+
puts "Update AD local cache by using openldap connector ..."
|
80
|
+
ny_ad=Ucert::AdTracker.new(:verbose=>@options.verbose)
|
81
|
+
ny_ad.ldap_connector="openldap"
|
82
|
+
ny_ad.ldap_connector_id=ldap_env["admin_user"]
|
83
|
+
ny_ad.ldap_connector_pass=ldap_env["admin_password"]
|
84
|
+
ny_ad.update_ad_cache("person")
|
85
|
+
ny_ad.update_ad_cache("computer")
|
86
|
+
# reload
|
87
|
+
ny_ad.parse_openldap_cache("person")
|
88
|
+
ny_ad.parse_openldap_cache("computer")
|
89
|
+
puts "Done update AD cache."
|
90
|
+
|
91
|
+
# Step 3 - connect to the 'cyber_idm' DB
|
92
|
+
db = Sequel.connect(:adapter => db_env["adapter"], :user => db_env["username"], :password => db_env["password"], :host => db_env["host"] , :database => db_env["database"])
|
93
|
+
|
94
|
+
# Step 4 - Reload the 'ad_users' table
|
95
|
+
puts "\n\nReload ad_users table ..."
|
96
|
+
db[:ad_users].truncate
|
97
|
+
ad_user_table = db[:ad_users]
|
98
|
+
my_users = Hash.new
|
99
|
+
ny_ad.ad_person_records.keys.map do |my_dn|
|
100
|
+
my_sam_id=ny_ad.get_dn_attribute("person",my_dn,"sAMAccountName")
|
101
|
+
my_email=ny_ad.get_dn_attribute("person",my_dn,"mail")
|
102
|
+
my_name=ny_ad.get_cn(my_dn)
|
103
|
+
my_dept=ny_ad.get_dn_attribute("person",my_dn,"department")
|
104
|
+
my_acct_code=ny_ad.get_dn_attribute("person",my_dn,"userAccountControl")
|
105
|
+
my_acct_status=ny_ad.cntl_code_2_property_flag(my_acct_code)
|
106
|
+
my_workstations=ny_ad.get_dn_attribute("person",my_dn,"userWorkstations")
|
107
|
+
my_membership=ny_ad.get_dn_attributes("person",my_dn,"memberOf")
|
108
|
+
my_users[my_dn] = Hash.new
|
109
|
+
my_users[my_dn]['DN'] = my_dn
|
110
|
+
my_users[my_dn]['NAME'] = my_name
|
111
|
+
my_users[my_dn]['SAM'] = my_sam_id
|
112
|
+
my_users[my_dn]['EMAIL'] = my_email
|
113
|
+
my_users[my_dn]['DEPARTMENT'] = my_dept
|
114
|
+
my_users[my_dn]['ACCOUNT_STATUS'] = my_acct_status
|
115
|
+
my_users[my_dn]['WORKSTATION'] = my_workstations
|
116
|
+
my_users[my_dn]['MEMBERSHIP'] = my_membership
|
117
|
+
end
|
118
|
+
my_users.values.map do |record|
|
119
|
+
puts "Insert record into ad_users table: #{record}" if @verbose
|
120
|
+
if ad_user_table.insert(:dn => record['DN'].to_s, :name => record['NAME'].to_s, :sam => record['SAM'].to_s, :email => record['EMAIL'].to_s, :department => record['DEPARTMENT'].to_s, :account_status => record['ACCOUNT_STATUS'].to_s, :work_station_info => record['WORKSTATION'].to_s, :membership => record['MEMBERSHIP'].to_s)
|
121
|
+
puts "Success!" if @verbose
|
122
|
+
else
|
123
|
+
puts "Insert fail. " if @verbose
|
124
|
+
end
|
125
|
+
end
|
126
|
+
puts "Done reload ad_users."
|
127
|
+
|
128
|
+
# Step 5 - Reload the 'ad_computers' table
|
129
|
+
puts "\n\nReload ad_computers table ..."
|
130
|
+
db[:ad_computers].truncate
|
131
|
+
ad_computer_table = db[:ad_computers]
|
132
|
+
my_computers = Hash.new
|
133
|
+
ny_ad.ad_computer_records.keys.map do |my_dn|
|
134
|
+
my_sam_id=ny_ad.get_dn_attribute("computer",my_dn,"sAMAccountName")
|
135
|
+
my_os=ny_ad.get_os_info(my_dn)
|
136
|
+
my_host=ny_ad.get_dn_attribute("computer",my_dn,"dNSHostName")
|
137
|
+
if ny_ad.known_hosts.key?(my_host)
|
138
|
+
my_ip=ny_ad.known_hosts[my_host]
|
139
|
+
else
|
140
|
+
my_ip=ny_ad.nslookup(my_host)
|
141
|
+
ny_ad.known_hosts[my_host]=my_ip
|
142
|
+
end
|
143
|
+
my_created=ny_ad.get_dn_attribute("computer",my_dn,"whenCreated")
|
144
|
+
my_computers[my_dn] = Hash.new
|
145
|
+
my_computers[my_dn]['DN'] = my_dn
|
146
|
+
my_computers[my_dn]['SAM'] = my_sam_id
|
147
|
+
my_computers[my_dn]['OS'] = my_os
|
148
|
+
my_computers[my_dn]['HOST'] = my_host
|
149
|
+
my_computers[my_dn]['IP'] = my_ip
|
150
|
+
my_computers[my_dn]['CREATED'] = my_created
|
151
|
+
end
|
152
|
+
my_computers.values.map do |record|
|
153
|
+
puts "Insert record into ad_computers table: #{record}" if @verbose
|
154
|
+
if ad_computer_table.insert(:dn => record['DN'].to_s, :sam => record['SAM'].to_s, :os => record['OS'].to_s, :host => record['HOST'].to_s, :ip => record['IP'].to_s, :created => record['CREATED'].to_s)
|
155
|
+
puts "Success!" if @verbose
|
156
|
+
else
|
157
|
+
puts "Insert fail." if @berbose
|
158
|
+
end
|
159
|
+
end
|
160
|
+
puts "Done reload ad_computers."
|
161
|
+
|
162
|
+
# Step 6 - clean up
|
163
|
+
db.disconnect
|
164
|
+
ny_ad = nil
|
data/lib/ucert.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#--
|
2
|
+
# ucert
|
3
|
+
#
|
4
|
+
# A Ruby library for enterprise user account certification / recertification / audit project
|
5
|
+
#
|
6
|
+
# Copyright (c) CMBNY Risk Department
|
7
|
+
#++
|
8
|
+
require 'ucert/utils/utils'
|
9
|
+
require 'ucert/ad_tracker'
|
10
|
+
require 'ucert/t24_tracker'
|
11
|
+
require 'ucert/go_contact_tracker'
|
12
|
+
require 'ucert/fis_prime_tracker'
|
13
|
+
require 'ucert/fis_egifts_tracker'
|
14
|
+
require 'ucert/alliance_swift_tracker'
|
15
|
+
require 'ucert/citidirect_securities_tracker'
|
16
|
+
require 'ucert/jpm_access_tracker'
|
17
|
+
require 'ucert/db_direct_tracker'
|
18
|
+
require 'ucert/sage100_tracker'
|
19
|
+
require 'ucert/vpn_tracker'
|
20
|
+
require 'ucert/swift_online_tracker'
|
21
|
+
require 'ucert/check21_tracker'
|
22
|
+
require 'ucert/cmbrun_ny_tracker'
|
23
|
+
require 'ucert/mantis_tracker'
|
24
|
+
require 'ucert/audiolog_tracker'
|
25
|
+
require 'ucert/citidirect_be_tracker'
|
26
|
+
require 'ucert/stb_tracker'
|
27
|
+
require 'ucert/equinix_tracker'
|
28
|
+
require 'ucert/citrix_sharefile_tracker'
|
29
|
+
require 'ucert/aix_tracker'
|
30
|
+
require 'ucert/frb_tracker'
|
31
|
+
require 'ucert/wms_tracker'
|
32
|
+
require 'ucert/yst_tracker'
|
33
|
+
require 'ucert/madison535_tracker'
|
34
|
+
require 'ucert/bloomberg_tracker'
|
35
|
+
require 'ucert/cvm_tracker'
|
36
|
+
require 'ucert/som_tracker'
|
37
|
+
require 'ucert/adp_payroll_tracker'
|
38
|
+
require 'ucert/clear_par_tracker'
|
39
|
+
|
40
|
+
module Ucert
|
41
|
+
|
42
|
+
NAME = "Ucert"
|
43
|
+
GEM = "ucert"
|
44
|
+
VERSION = File.dirname(__FILE__) + "/../version.txt"
|
45
|
+
@verbose = false
|
46
|
+
# Module instance variable to keep track of the DN record change in the AD
|
47
|
+
@ad_delta = File.dirname(__FILE__) + "/../data/ad/ad_delta.txt"
|
48
|
+
|
49
|
+
class << self
|
50
|
+
attr_accessor :ad_delta, :verbose
|
51
|
+
|
52
|
+
# Simple parser for the project version file
|
53
|
+
def read_ver
|
54
|
+
ver=Hash.new
|
55
|
+
f=File.open(VERSION,'r')
|
56
|
+
f.each do |line|
|
57
|
+
line.chomp!
|
58
|
+
case line
|
59
|
+
when /^(\s)*#/
|
60
|
+
next
|
61
|
+
when /\=/
|
62
|
+
entry=line.split("=").map! {|x| x.strip}
|
63
|
+
ver[entry[0]]=entry[1]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
f.close
|
67
|
+
return ver
|
68
|
+
end
|
69
|
+
|
70
|
+
def banner
|
71
|
+
ver=read_ver
|
72
|
+
art=""
|
73
|
+
string = "-"*80 + "\n" + art + "\n" + "Version: " + ver["version"] + "\tRelease Date: " + ver["date"] + "\nDesigned and developed by: " + ver["author"] + "\nEmail: " + ver["email"] + "\tLinkedIn: " + ver["linkedin"] + "\n" + "-"*80
|
74
|
+
end
|
75
|
+
|
76
|
+
# To do
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,694 @@
|
|
1
|
+
#--
|
2
|
+
# ucert
|
3
|
+
#
|
4
|
+
# A Ruby library for enterprise user account certification / recertification / audit project
|
5
|
+
#
|
6
|
+
# Copyright (c) CMBNY Risk Department
|
7
|
+
#++
|
8
|
+
require "net/ldap"
|
9
|
+
|
10
|
+
# Class to handle the ative directory local data cache repository. Note this
|
11
|
+
# class depends on the 3rd party program 'openldap' @ www.openldap.org
|
12
|
+
class Ucert::AdTracker
|
13
|
+
include Ucert::Utils
|
14
|
+
|
15
|
+
attr_accessor :verbose,:ldap_connector,:ldapsearch_argv_1,:ldapsearch_argv_2,:ldap_connector_id, :ldap_connector_pass, \
|
16
|
+
:ldap_host, :ldap_port, :ldap_base, :ad_delta_map, :ad_delta, :refresh_dns_records
|
17
|
+
attr_reader :ad_person_records,:ad_computer_records, \
|
18
|
+
:ldapsearch_cache_person,:ldapsearch_cache_computer,:program_ldapsearch,:acct_cntl_code,:known_hosts
|
19
|
+
|
20
|
+
# Instance default variables
|
21
|
+
def initialize (params ={})
|
22
|
+
# Specify the dependency 3rd party application 'ldapsearch' installation path
|
23
|
+
@verbose=params.fetch(:verbose, false)
|
24
|
+
@refresh_dns_records=params.fetch(:refresh_dns_records, false) # params to turn on/off DNS records refreshment. Use with caution as it's time intensive task.
|
25
|
+
@program_ldapsearch = "/usr/bin/ldapsearch"
|
26
|
+
# Specify default ldap connector and connecting credential; currently we support: 1) 'openldap'
|
27
|
+
@ldap_connector = "openldap"
|
28
|
+
@ldap_connector_id=params.fetch(:ldap_connector_id,"")
|
29
|
+
@ldap_connector_pass=params.fetch(:ldap_connector_pass,"")
|
30
|
+
@ldap_host = params.fetch(:ldap_host, "CMBNY-OADC2.ny.cmbchina.com")
|
31
|
+
@ldap_port = params.fetch(:ldap_port, "389")
|
32
|
+
@ldap_base = params.fetch(:ldap_base, "DC=ny,DC=cmbchina,DC=com")
|
33
|
+
# Specify the dependency 3rd party application 'ldapsearh' command argument options
|
34
|
+
#@ldapsearch_argv_1 = " -b \"DC=ny,DC=cmbchina,DC=com\" -h CMBNY-OADC2.ny.cmbchina.com -p 389 -D "
|
35
|
+
@ldapsearch_argv_1 = " -b " + @ldap_base + " -h " + @ldap_host + " -p " + @ldap_port + " -D "
|
36
|
+
@ldapsearch_argv_2 = " -s sub \"objectcategory="
|
37
|
+
# Specify the cache file location for 'ldapsearch'
|
38
|
+
@ldapsearch_cache_person = File.dirname(__FILE__) + "/../../data/ad/ldap_person.txt"
|
39
|
+
@ldapsearch_cache_computer = File.dirname(__FILE__) + "/../../data/ad/ldap_computer.txt"
|
40
|
+
# Specify the local hosts cache file for fast DNS resolving
|
41
|
+
@hosts_cache = File.dirname(__FILE__) + "/../../data/ad/hosts"
|
42
|
+
@known_hosts=load_known_hosts(@hosts_cache)
|
43
|
+
# AD Delta detection
|
44
|
+
@ad_delta_map = File.dirname(__FILE__) + "/../../data/ad/ad_delta.txt"
|
45
|
+
@ad_delta = load_known_user_map_from_file (@ad_delta_map)
|
46
|
+
# class instance variables to keep track of the AD record changes
|
47
|
+
@ad_person_records = Hash.new
|
48
|
+
@ad_computer_records = Hash.new
|
49
|
+
# Refer to microsoft KB 'How to use the UserAccountControl flags' - https://support.microsoft.com/en-us/kb/305144
|
50
|
+
@acct_cntl_code={"SCRIPT"=>1, "ACCOUNTDISABLE"=>2, "HOMEDIR_REQUIRED"=>4, "LOCKOUT"=>5, "PASSWD_NOTREQD"=>6, \
|
51
|
+
"PASSWD_CANT_CHANGE"=>7, "ENCRYPTED_TEXT_PWD_ALLOWED"=>8, "TEMP_DUPLICATE_ACCOUNT"=>9, "NORMAL_ACCOUNT"=>10, \
|
52
|
+
"'INTERDOMAIN_TRUST_ACCOUNT'"=>12, "WORKSTATION_TRUST_ACCOUNT"=>13, "SERVER_TRUST_ACCOUNT"=>14, \
|
53
|
+
"DONT_EXPIRE_PASSWORD"=>17, "MNS_LOGON_ACCOUNT"=>18, "SMARTCARD_REQUIRED"=>19, \
|
54
|
+
"TRUSTED_FOR_DELEGATION"=>20, "NOT_DELEGATED"=>21, "USE_DES_KEY_ONLY"=>22, "DONT_REQ_PREAUTH"=>23, \
|
55
|
+
"PASSWORD_EXPIRED"=>24, "TRUSTED_TO_AUTH_FOR_DELEGATION"=>25, "PARTIAL_SECRETS_ACCOUNT"=>27}
|
56
|
+
# loading the instance variables
|
57
|
+
load_ad(@ldap_connector)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Load AD tracker instance variables from the cache files; re-create the cache files if non-existing.
|
61
|
+
def load_ad (connector)
|
62
|
+
begin
|
63
|
+
case connector
|
64
|
+
when "openldap"
|
65
|
+
update_openldap_cache("person") unless File.exist?(@ldapsearch_cache_person)
|
66
|
+
parse_openldap_cache("person")
|
67
|
+
update_openldap_cache("computer") unless File.exist?(@ldapsearch_cache_computer)
|
68
|
+
parse_openldap_cache("computer")
|
69
|
+
else
|
70
|
+
#do nothing
|
71
|
+
end
|
72
|
+
rescue => ee
|
73
|
+
puts "Exception on method #{__method__}: #{ee}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# wrapper to Net::LDAP ldap.bind method
|
78
|
+
def is_ldap_bind?
|
79
|
+
begin
|
80
|
+
ldap = Net::LDAP.new
|
81
|
+
ldap.host = @ldap_host
|
82
|
+
ldap.port = @ldap_port
|
83
|
+
ldap.auth @ldap_connector_id, @ldap_connector_pass
|
84
|
+
if ldap.bind
|
85
|
+
puts "LDAP bind to #{@ldap_host} successfully!"
|
86
|
+
return true
|
87
|
+
else
|
88
|
+
puts "LDAP bind to #{@ldap_host} fail!"
|
89
|
+
return false
|
90
|
+
end
|
91
|
+
rescue => ee
|
92
|
+
puts "Exception on method #{__method__}: #{ee}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# method to update the active directory local cache file
|
97
|
+
def update_ad_cache (opt)
|
98
|
+
begin
|
99
|
+
puts "Start AD cache file update process for objectcategory: #{opt}"
|
100
|
+
case @ldap_connector
|
101
|
+
when "openldap"
|
102
|
+
my_dn=sid_2_dn(@ldap_connector_id)
|
103
|
+
abort "Error. Unknown user ID: #{@ldap_connector_id}" if my_dn.nil?
|
104
|
+
case opt
|
105
|
+
when "person"
|
106
|
+
# cmd = @program_ldapsearch + @ldapsearch_argv_1 + "\"" + my_dn + "\" -w " + @ldap_connector_pass + @ldapsearch_argv_2 + opt + "\" > " + @ldapsearch_cache_person
|
107
|
+
cmd = @program_ldapsearch + @ldapsearch_argv_1 + "\"" + @ldap_connector_id + "\" -w " + @ldap_connector_pass + @ldapsearch_argv_2 + opt + "\" > " + @ldapsearch_cache_person
|
108
|
+
when "computer"
|
109
|
+
# cmd = @program_ldapsearch + @ldapsearch_argv_1 + "\"" + my_dn + "\" -w " + @ldap_connector_pass + @ldapsearch_argv_2 + opt + "\" > " + @ldapsearch_cache_computer
|
110
|
+
cmd = @program_ldapsearch + @ldapsearch_argv_1 + "\"" + @ldap_connector_id + "\" -w " + @ldap_connector_pass + @ldapsearch_argv_2 + opt + "\" > " + @ldapsearch_cache_computer
|
111
|
+
else
|
112
|
+
raise "Error - unknown objectcategory: #{opt}"
|
113
|
+
end
|
114
|
+
else
|
115
|
+
raise "Error - unknown LDAP connector: #{@ldap_connector}"
|
116
|
+
end
|
117
|
+
# Test connection before the update
|
118
|
+
if is_ldap_bind?
|
119
|
+
puts "Execute shell command: #{cmd}" if @verbose
|
120
|
+
system(cmd)
|
121
|
+
puts "Done!"
|
122
|
+
end
|
123
|
+
sleep (2)
|
124
|
+
rescue => ee
|
125
|
+
puts "Exception on method #{__method__}: #{ee}"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
alias_method :update, :update_ad_cache
|
129
|
+
|
130
|
+
# method to parse the openldap cache file, save the record into a hash data structure
|
131
|
+
def parse_openldap_cache (opt)
|
132
|
+
begin
|
133
|
+
case opt
|
134
|
+
when "person"
|
135
|
+
file = @ldapsearch_cache_person
|
136
|
+
when "computer"
|
137
|
+
file = @ldapsearch_cache_computer
|
138
|
+
else
|
139
|
+
raise "Unkown log file type: #{opt}"
|
140
|
+
end
|
141
|
+
puts "Start working on openldap log file: #{file}" if @verbose
|
142
|
+
raise "File not found. Please check your path again: #{file}" unless File.exist?(file)
|
143
|
+
f_cache = File.open(file, 'r:ISO-8859-1:UTF-8')
|
144
|
+
# recording bit to flag start / end of new ad record
|
145
|
+
recording=false
|
146
|
+
ad_objs=Hash.new
|
147
|
+
dn_key=String.new
|
148
|
+
# flag to check next line in file where the dn string may be split over to
|
149
|
+
dn_key_nextline_check=false
|
150
|
+
f_cache.each do |line|
|
151
|
+
line.chomp!
|
152
|
+
if dn_key_nextline_check
|
153
|
+
puts "Perform DN Key next line check for its completeness: #{dn_key}" if @verbose
|
154
|
+
if line =~ /^\s/
|
155
|
+
puts "Incomplete DN Key found: #{dn_key}" if @verbose
|
156
|
+
dn_key=dn_key+line.sub(/^\s/,'')
|
157
|
+
ad_objs[dn_key]=Array.new
|
158
|
+
dn_key_nextline_check=false
|
159
|
+
recording=true
|
160
|
+
next
|
161
|
+
else
|
162
|
+
puts "DN Key found completed: #{dn_key}" if @verbose
|
163
|
+
dn_key_nextline_check=false
|
164
|
+
ad_objs[dn_key]=Array.new
|
165
|
+
dn_key_nextline_check=false
|
166
|
+
recording=true
|
167
|
+
end
|
168
|
+
end
|
169
|
+
if line =~ /^dn\:/
|
170
|
+
puts "DN Key found in line: #{line}" if @verbose
|
171
|
+
dn_key=line.split(':')[1].sub(/^\s/,'')
|
172
|
+
puts "DN found: #{dn_key}" if @verbose
|
173
|
+
dn_key_nextline_check=true
|
174
|
+
next
|
175
|
+
end
|
176
|
+
if line.nil? or line.empty?
|
177
|
+
puts "Done processing one record." if @verbose
|
178
|
+
#sleep(2)
|
179
|
+
recording=false
|
180
|
+
next
|
181
|
+
end
|
182
|
+
if recording
|
183
|
+
puts "Start recording for record: #{dn_key}" if @verbose
|
184
|
+
if line=~ /^\s/
|
185
|
+
puts "Modify last attribute for its incompleteness: #{ad_objs[dn_key].last} - #{line} " if @verbose
|
186
|
+
ad_objs[dn_key][-1] = ad_objs[dn_key].last + line.sub(/^\s/,'')
|
187
|
+
else
|
188
|
+
puts "Adding new record attribute: #{line}" if @verbose
|
189
|
+
ad_objs[dn_key].push(line)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
f_cache.close
|
194
|
+
case opt
|
195
|
+
when "person"
|
196
|
+
@ad_person_records=ad_objs
|
197
|
+
when "computer"
|
198
|
+
@ad_computer_records=ad_objs
|
199
|
+
save_hosts # refresh the hosts file
|
200
|
+
else
|
201
|
+
raise "Unkown log file type: #{opt}"
|
202
|
+
end
|
203
|
+
puts "Done processing the cache file: #{file}" if @verbose
|
204
|
+
rescue => ee
|
205
|
+
puts "Exception on method #{__method__}: #{ee}"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# generic text string search of ad local cache for matched record, return the record primary key, i.e. DN
|
210
|
+
def ad_search_by_text (keyword,opt="person")
|
211
|
+
begin
|
212
|
+
puts "Begin search on the cache for: #{keyword}" if @verbose
|
213
|
+
return nil if keyword.nil?
|
214
|
+
keywords=keyword.downcase.split(/(\,|\s|\;|\&|\!|\@|\#|\$|\%|\^|\*|\(|\)|\-|\=|\+|\=|\{|\}|\[|\]|\:|\~)/) \
|
215
|
+
- [" ", "", nil, ",", ";", "&", "!", "@", "#", "$", "%", "^", "*", "(", ")", "-", "+", "=", "{", "}", "[", "]", ":" "~"]
|
216
|
+
#txt.downcase!
|
217
|
+
case opt
|
218
|
+
when "computer"
|
219
|
+
@ad_computer_records.each do |key, val|
|
220
|
+
puts "Searching computer cache data." if @verbose
|
221
|
+
val.map do |entry|
|
222
|
+
every=entry.to_s.downcase
|
223
|
+
success=keywords.inject(true) {|found,word| break unless found; every.include?(word) && found; }
|
224
|
+
if success
|
225
|
+
return key
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
when "person"
|
230
|
+
@ad_person_records.each do |key, val|
|
231
|
+
puts "Searching person cache data." if @verbose
|
232
|
+
word_1=keywords.join(" ")
|
233
|
+
word_2=keywords.reverse.join(" ")
|
234
|
+
val.map do |entry|
|
235
|
+
puts "First order searching: #{entry} for #{word_1} or #{word_2}" if @verbose
|
236
|
+
every=entry.to_s.downcase
|
237
|
+
if every.include?(" " + word_1) or every.include?(" " + word_2)
|
238
|
+
puts "Best match found: #{entry}" if @verboseirb
|
239
|
+
return key
|
240
|
+
elsif every.include?(word_1) or every.include?(word_2)
|
241
|
+
puts "Best match found: #{entry}" if @verbose
|
242
|
+
return key
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
@ad_person_records.each do |key, val|
|
247
|
+
val.map do |entry|
|
248
|
+
puts "Secondary order searching: #{entry}" if @verbose
|
249
|
+
every=entry.to_s.downcase
|
250
|
+
success2=keywords.inject(true) {|found,word| break unless found; every.include?(word) && found; }
|
251
|
+
if success2
|
252
|
+
puts "Close match found: #{entry}" if @verbose
|
253
|
+
return key
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
else
|
258
|
+
raise "Unknow cache: #{opt}"
|
259
|
+
end
|
260
|
+
return nil
|
261
|
+
rescue => ee
|
262
|
+
puts "Exception on method #{__method__}: #{ee}"
|
263
|
+
end
|
264
|
+
end
|
265
|
+
alias_method :search, :ad_search_by_text
|
266
|
+
|
267
|
+
# generic text string search of ad local cache for matched records. Input is
|
268
|
+
# a keyword string, return the record primary keys, i.e. multiple DNs in an array.
|
269
|
+
def ad_searches_by_text (keyword,opt="person",max=10)
|
270
|
+
begin
|
271
|
+
puts "Begin searches on the cache for: #{keyword}" if @verbose
|
272
|
+
return nil if keyword.nil?
|
273
|
+
keywords=keyword.downcase.split(/(\,|\s|\;|\&|\!|\@|\#|\$|\%|\^|\*|\(|\)|\-|\=|\+|\=|\{|\}|\[|\]|\:|\~)/) \
|
274
|
+
- [" ", "", nil, ",", ";", "&", "!", "@", "#", "$", "%", "^", "*", "(", ")", "-", "+", "=", "{", "}", "[", "]", ":" "~"]
|
275
|
+
search_result=Array.new
|
276
|
+
case opt
|
277
|
+
when "computer"
|
278
|
+
cnt=0
|
279
|
+
@ad_computer_records.each do |key, val|
|
280
|
+
puts "Searching computer cache data on: #{key}" if @verbose
|
281
|
+
val.map do |entry|
|
282
|
+
break if cnt >= max # limit returned records to max
|
283
|
+
every = entry.to_s.downcase
|
284
|
+
success=keywords.inject(true) {|found,word| break unless found; every.include?(word) && found; }
|
285
|
+
if success
|
286
|
+
puts "Match found: #{entry}" if @verbose
|
287
|
+
search_result.push(key)
|
288
|
+
cnt+=1
|
289
|
+
break
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
when "person"
|
294
|
+
cnt=0
|
295
|
+
# Perform 1st order search of CN portion of DN only for the best match
|
296
|
+
@ad_person_records.keys.map do |x|
|
297
|
+
puts "Searching person DN entry for #{key}" if @verbose
|
298
|
+
word_1=keywords.join(" ").downcase
|
299
|
+
word_2=keywords.reverse.join(" ").downcase
|
300
|
+
y=get_cn(x).downcase
|
301
|
+
if y.include?(word_1) or y.include?(word_2)
|
302
|
+
puts "Match found: #{x}" if @verbose
|
303
|
+
search_result.push(x)
|
304
|
+
cnt+=1
|
305
|
+
end
|
306
|
+
end
|
307
|
+
# Perform 2nd order search of complete keyword string on person record attributes
|
308
|
+
@ad_person_records.each do |key, val|
|
309
|
+
puts "Searching person cache data on: #{key}" if @verbose
|
310
|
+
word_1=keywords.join(" ")
|
311
|
+
word_2=keywords.reverse.join(" ")
|
312
|
+
val.map do |entry|
|
313
|
+
break if cnt >= max # limit returned records to max
|
314
|
+
every = entry.to_s.downcase
|
315
|
+
if every.include?(word_1) or every.include?(word_2)
|
316
|
+
puts "Match found: #{entry}" if @verbose
|
317
|
+
search_result.push(key)
|
318
|
+
cnt+=1
|
319
|
+
break
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
# Perform 3rd order search of partial keyword string on the person record attributes
|
324
|
+
@ad_person_records.each do |key, val|
|
325
|
+
puts "Searching person cache data on: #{key}" if @verbose
|
326
|
+
val.map do |entry|
|
327
|
+
break if cnt >= max # limit returned records to max
|
328
|
+
every = entry.to_s.downcase
|
329
|
+
success=keywords.inject(true) {|found,word| break unless found; every.include?(word) && found; }
|
330
|
+
#entry_clean=entry.chomp
|
331
|
+
#if entry_clean.downcase.include?(txt)
|
332
|
+
if success
|
333
|
+
puts "Match found: #{entry}" if @verbose
|
334
|
+
search_result.push(key)
|
335
|
+
cnt+=1
|
336
|
+
break
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
#
|
341
|
+
else
|
342
|
+
raise "Unknow cache: #{opt}"
|
343
|
+
end
|
344
|
+
return search_result.uniq
|
345
|
+
rescue => ee
|
346
|
+
puts "Exception on method #{__method__}: #{ee}"
|
347
|
+
return search_result
|
348
|
+
end
|
349
|
+
end
|
350
|
+
alias_method :searches, :ad_searches_by_text
|
351
|
+
|
352
|
+
# Retrieve the specific dn object
|
353
|
+
def get_ad_record (dn)
|
354
|
+
begin
|
355
|
+
return @ad_person_records[dn] if @ad_person_records.key?(dn)
|
356
|
+
return @ad_computer_records[dn] if @ad_computer_records.key?(dn)
|
357
|
+
return "Unfound"
|
358
|
+
rescue => ee
|
359
|
+
puts "Exception on method #{__method__}: #{ee}"
|
360
|
+
return "Unfound"
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
# Retrieve the specific attribute from the dn's attribute collection
|
365
|
+
def get_dn_attribute (opt="person",dn,attr_name)
|
366
|
+
begin
|
367
|
+
case opt
|
368
|
+
when "person"
|
369
|
+
attrs=@ad_person_records[dn] if @ad_person_records.key?(dn)
|
370
|
+
when "computer"
|
371
|
+
attrs=@ad_computer_records[dn] if @ad_computer_records.key?(dn)
|
372
|
+
else
|
373
|
+
raise "Error - unknown objectcategory: #{opt}"
|
374
|
+
end
|
375
|
+
#puts attrs
|
376
|
+
attrs.map do |line|
|
377
|
+
(key,val)=line.chomp.split(':')
|
378
|
+
if key==attr_name
|
379
|
+
return val.strip
|
380
|
+
end
|
381
|
+
end
|
382
|
+
return nil
|
383
|
+
rescue => ee
|
384
|
+
puts "Exception on method #{__method__}: #{ee}"
|
385
|
+
return nil
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
# Retrieve the attributes from the dn's attribute collection; the output is the matchs inside an array
|
390
|
+
def get_dn_attributes (opt,dn,attr_name)
|
391
|
+
begin
|
392
|
+
founds = Array.new
|
393
|
+
case opt
|
394
|
+
when "person"
|
395
|
+
attrs=@ad_person_records[dn] if @ad_person_records.key?(dn)
|
396
|
+
when "computer"
|
397
|
+
attrs=@ad_computer_records[dn] if @ad_computer_records.key?(dn)
|
398
|
+
else
|
399
|
+
raise "Error - unknown objectcategory: #{opt}"
|
400
|
+
end
|
401
|
+
#puts attrs
|
402
|
+
attrs.map do |line|
|
403
|
+
(key,val)=line.chomp.split(':')
|
404
|
+
if key==attr_name
|
405
|
+
founds.push(val.strip)
|
406
|
+
end
|
407
|
+
end
|
408
|
+
return founds
|
409
|
+
rescue => ee
|
410
|
+
puts "Exception on method #{__method__}: #{ee}"
|
411
|
+
return nil
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
# Lookup DN by sAMAccountName attribute
|
416
|
+
def sid_2_dn (sid)
|
417
|
+
begin
|
418
|
+
@ad_person_records.keys.map do |dn|
|
419
|
+
attrs=@ad_person_records[dn]
|
420
|
+
attrs.map do |line|
|
421
|
+
(key,val)=line.chomp.split(':')
|
422
|
+
if val.strip.upcase==sid.strip.upcase
|
423
|
+
return dn
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
return nil
|
428
|
+
rescue => ee
|
429
|
+
puts "Exception on method #{__method__}: #{ee}"
|
430
|
+
return "Unfound"
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
# Retrieve the first CN "Full Name" for the DN
|
435
|
+
def get_cn (dn)
|
436
|
+
begin
|
437
|
+
attrs=@ad_person_records[dn] if @ad_person_records.key?(dn)
|
438
|
+
attrs=@ad_computer_records[dn] if @ad_computer_records.key?(dn)
|
439
|
+
attrs.map do |line|
|
440
|
+
(key,val)=line.chomp.split(':')
|
441
|
+
if key=="cn"
|
442
|
+
return val.strip
|
443
|
+
end
|
444
|
+
end
|
445
|
+
return "Unfound"
|
446
|
+
rescue => ee
|
447
|
+
puts "Exception on method #{__method__}: #{ee}"
|
448
|
+
return "Unfound"
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
# String manipulation to extract the first CN from the DN
|
453
|
+
def extract_first_cn (dn)
|
454
|
+
begin
|
455
|
+
return nil if dn.nil? or dn.empty?
|
456
|
+
dn.split(',').map do |x|
|
457
|
+
return x if x=~ /^cn=/i
|
458
|
+
end
|
459
|
+
rescue => ee
|
460
|
+
puts "Exception on method #{__method__}: #{ee}"
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
# Retrieve a list of CNs i.e. "Full Name" for the dns
|
465
|
+
def get_cns (dns)
|
466
|
+
begin
|
467
|
+
result=Array.new
|
468
|
+
dns.map do |dn|
|
469
|
+
cn=get_cn(dn)
|
470
|
+
result.push(cn)
|
471
|
+
end
|
472
|
+
return result
|
473
|
+
rescue => ee
|
474
|
+
puts "Exception on method #{__method__}: #{ee}"
|
475
|
+
return result
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
def get_os_info (dn)
|
480
|
+
begin
|
481
|
+
puts "Retrieve Operation System information for: #{dn}" if @verbose
|
482
|
+
os_info = String.new
|
483
|
+
attrs=@ad_computer_records[dn] if @ad_computer_records.key?(dn)
|
484
|
+
attrs.map do |line|
|
485
|
+
(key,val)=line.chomp.split(':')
|
486
|
+
case key
|
487
|
+
when "operatingSystem"
|
488
|
+
os_info += val.strip
|
489
|
+
when "operatingSystemVersion"
|
490
|
+
os_info += val
|
491
|
+
when "operatingSystemServicePack"
|
492
|
+
os_info += val
|
493
|
+
end
|
494
|
+
end
|
495
|
+
return os_info
|
496
|
+
rescue => ee
|
497
|
+
puts "Exception on method #{__method__}: #{ee}" if @verbose
|
498
|
+
return "Unfound"
|
499
|
+
end
|
500
|
+
end
|
501
|
+
alias_method :get_os, :get_os_info
|
502
|
+
|
503
|
+
# Convertion of MS UserAccountControl code in decimal value back to 'Property flag'
|
504
|
+
def cntl_code_2_property_flag (code)
|
505
|
+
begin
|
506
|
+
puts "Perform user account status lookup for user account control code: #{code}" if @verbose
|
507
|
+
code_2_status = Hash.new
|
508
|
+
@acct_cntl_code.each { |k,v| code_2_status[v]=k }
|
509
|
+
account_status = Array.new
|
510
|
+
b_code = code.to_i.to_s(2)
|
511
|
+
a_code = b_code.split('').reverse
|
512
|
+
(0...a_code.count).each do |x|
|
513
|
+
if a_code[x] === "1"
|
514
|
+
status = code_2_status[x+1]
|
515
|
+
account_status.push(status)
|
516
|
+
end
|
517
|
+
end
|
518
|
+
return account_status.join(' + ')
|
519
|
+
rescue => ee
|
520
|
+
puts "Exception on method #{__method__}: #{ee}" if @verbose
|
521
|
+
return "Unknown"
|
522
|
+
end
|
523
|
+
end
|
524
|
+
alias_method :code_2_flag, :cntl_code_2_property_flag
|
525
|
+
|
526
|
+
# Print out Adstore cache record in tab-delimited file format
|
527
|
+
def print (opt)
|
528
|
+
begin
|
529
|
+
puts "Print out the Active Directory Cache #{opt} record in tab-delimited format: " if @verbose
|
530
|
+
case opt
|
531
|
+
when "computer"
|
532
|
+
@ad_computer_records.keys.map do |my_dn|
|
533
|
+
my_sam_id=get_dn_attribute(opt,my_dn,"sAMAccountName")
|
534
|
+
my_os=get_os_info(my_dn)
|
535
|
+
my_host=get_dn_attribute(opt,my_dn,"dNSHostName")
|
536
|
+
my_membership=get_dn_attributes(opt,my_dn,"memberOf")
|
537
|
+
if @known_hosts.key?(my_host)
|
538
|
+
my_ip=@known_hosts[my_host]
|
539
|
+
else
|
540
|
+
my_ip=nslookup(my_host) if @refresh_dns_records
|
541
|
+
@known_hosts[my_host]=my_ip
|
542
|
+
end
|
543
|
+
my_created=get_dn_attribute(opt,my_dn,"whenCreated")
|
544
|
+
puts "#{my_dn}|#{my_sam_id}|#{my_os}|#{my_host}|#{my_ip}|#{my_created}|#{my_membership}"
|
545
|
+
end
|
546
|
+
# Save the known hosts into cache file for future reference
|
547
|
+
save_hosts(@hosts_cache)
|
548
|
+
when "person"
|
549
|
+
@ad_person_records.keys.map do |my_dn|
|
550
|
+
my_sam_id=get_dn_attribute(opt,my_dn,"sAMAccountName")
|
551
|
+
my_email=get_dn_attribute(opt,my_dn,"mail")
|
552
|
+
my_name=get_cn(my_dn)
|
553
|
+
my_dept=get_dn_attribute(opt,my_dn,"department")
|
554
|
+
my_acct_code=get_dn_attribute(opt,my_dn,"userAccountControl")
|
555
|
+
my_acct_status=cntl_code_2_property_flag(my_acct_code)
|
556
|
+
my_workstations=get_dn_attribute(opt,my_dn,"userWorkstations")
|
557
|
+
my_membership=get_dn_attributes(opt,my_dn,"memberOf")
|
558
|
+
puts "#{my_dn}|#{my_name}|#{my_sam_id}|#{my_email}|#{my_dept}|#{my_acct_status}|#{my_workstations}|#{my_membership}"
|
559
|
+
end
|
560
|
+
else
|
561
|
+
raise "Unknow cache table: #{opt} \t Please check your input Type again."
|
562
|
+
end
|
563
|
+
return nil
|
564
|
+
rescue => ee
|
565
|
+
puts "Exception on method #{__method__}: #{ee}"
|
566
|
+
end
|
567
|
+
end
|
568
|
+
alias_method :dump, :print # make the two method call the same function
|
569
|
+
|
570
|
+
# refresh @known_hosts instance based on the lastest ad_computer_records
|
571
|
+
def refresh_hosts
|
572
|
+
begin
|
573
|
+
@ad_computer_records.keys.map do |my_dn|
|
574
|
+
my_sam_id=get_dn_attribute("computer",my_dn,"sAMAccountName")
|
575
|
+
my_os=get_os_info(my_dn)
|
576
|
+
my_host=get_dn_attribute("computer",my_dn,"dNSHostName")
|
577
|
+
my_membership=get_dn_attributes("computer",my_dn,"memberOf")
|
578
|
+
if @known_hosts.key?(my_host)
|
579
|
+
my_ip=@known_hosts[my_host]
|
580
|
+
else
|
581
|
+
my_ip=nslookup(my_host)
|
582
|
+
@known_hosts[my_host]=my_ip
|
583
|
+
end
|
584
|
+
end
|
585
|
+
rescue => ee
|
586
|
+
puts "Exception on method #{__method__}: #{ee}"
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
# Save the known hosts table into cache file
|
591
|
+
def save_hosts(f_hosts=@hosts_cache)
|
592
|
+
begin
|
593
|
+
puts "Save the hosts table from memory to file: #{f_hosts} ..." if @verbose
|
594
|
+
# update the @known_hosts instance
|
595
|
+
refresh_hosts if @refresh_dns_records
|
596
|
+
# Write the @known_hosts instance back to hosts file
|
597
|
+
timestamp=Time.now
|
598
|
+
f=File.open(f_hosts, 'w')
|
599
|
+
f.write "# local hosts file created by the #{self.class} class #{__method__} method at: #{timestamp}"
|
600
|
+
(@known_hosts.keys-[nil]).sort.map do |key|
|
601
|
+
unless key =~ /\d+\.\d+\.\d+\.\d+/
|
602
|
+
f.write "\n#{key}\t#{@known_hosts[key]}"
|
603
|
+
end
|
604
|
+
end
|
605
|
+
f.close
|
606
|
+
puts "local host repository is successfully saved to: #{f_hosts}" if @verbose
|
607
|
+
rescue => ee
|
608
|
+
puts "Exception on method #{__method__}: #{ee}"
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
612
|
+
# Save the AD change back into cache file
|
613
|
+
def save_delta (f_delta=@ad_delta_map)
|
614
|
+
begin
|
615
|
+
puts "Save the AD Delta table from memory to file: #{f_delta} ..." if @verbose
|
616
|
+
timestamp=Time.now
|
617
|
+
f=File.open(f_delta, 'w')
|
618
|
+
f.write "# AD Delta created by the #{self.class} class #{__method__} method at: #{timestamp}"
|
619
|
+
@ad_delta.each do |key, value|
|
620
|
+
unless key =~ /\d+\.\d+\.\d+\.\d+/ or key.nil?
|
621
|
+
f.write "\n#{key}|#{value}"
|
622
|
+
end
|
623
|
+
end
|
624
|
+
f.close
|
625
|
+
puts "AD delta records are successfully saved to: #{f_hosts}" if @verbose
|
626
|
+
rescue => ee
|
627
|
+
puts "Exception on method #{__method__}: #{ee}"
|
628
|
+
end
|
629
|
+
end
|
630
|
+
|
631
|
+
# Load the hosts cache file for fast DNS resolving
|
632
|
+
def load_known_hosts (f_hosts=@file_hosts)
|
633
|
+
puts "Loading local hosts from file: #{f_hosts} ..." if @verbose
|
634
|
+
begin
|
635
|
+
known_hosts=Hash.new
|
636
|
+
f=File.open(f_hosts, 'r')
|
637
|
+
f.each do |line|
|
638
|
+
next unless line =~ /\d+\.\d+\.\d+\.\d+/
|
639
|
+
entry=line.chomp.split(%r{\t+|\s+|\,})
|
640
|
+
key=entry[0].downcase
|
641
|
+
value=entry[1]
|
642
|
+
puts "Loading value pair: #{key} - #{value}" if @verbose
|
643
|
+
known_hosts[key] = Hash.new unless known_hosts.key?(key)
|
644
|
+
known_hosts[key]= value
|
645
|
+
end
|
646
|
+
f.close
|
647
|
+
return known_hosts
|
648
|
+
rescue => ee
|
649
|
+
puts "Exception on method #{__method__}: #{ee}"
|
650
|
+
return Hash.new
|
651
|
+
end
|
652
|
+
end
|
653
|
+
|
654
|
+
# Local DNS reverse lookup
|
655
|
+
def local_ip_2_host (ip)
|
656
|
+
puts "Perform local IP to hostname lookup on IP: #{ip}" if @verbose
|
657
|
+
begin
|
658
|
+
ip.chomp!
|
659
|
+
raise "Invalid IP address format: #{ip}" unless ip =~ /\d+\.\d+\.\d+\.\d+/
|
660
|
+
@known_hosts.to_a.reverse.to_h.each do |key,val|
|
661
|
+
return key.to_s if val == ip
|
662
|
+
end
|
663
|
+
return nil
|
664
|
+
rescue => ee
|
665
|
+
puts "Exception on method #{__method__}: #{ee}"
|
666
|
+
return nil
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
# SAM ID to DN reverse lookup
|
671
|
+
def sam_2_dn (id)
|
672
|
+
puts "Perform SAM ID to DN lookup for: #{id}" if @verbose
|
673
|
+
begin
|
674
|
+
id.strip!
|
675
|
+
@ad_person_records.keys.map do |my_dn|
|
676
|
+
sam_id=get_dn_attribute("person",my_dn,"sAMAccountName")
|
677
|
+
if sam_id == id
|
678
|
+
return my_dn
|
679
|
+
end
|
680
|
+
end
|
681
|
+
@ad_computer_records.keys.map do |my_dn|
|
682
|
+
sam_id=get_dn_attribute("computer",my_dn,"sAMAccountName")
|
683
|
+
if sam_id == id
|
684
|
+
return my_dn
|
685
|
+
end
|
686
|
+
end
|
687
|
+
return nil
|
688
|
+
rescue => ee
|
689
|
+
puts "Exception on method #{__method__}: #{ee}"
|
690
|
+
return nil
|
691
|
+
end
|
692
|
+
end
|
693
|
+
|
694
|
+
end
|