racf 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +8 -0
- data/.rspec +2 -0
- data/.travis.yml +18 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +40 -0
- data/README.rdoc +56 -0
- data/Rakefile +9 -0
- data/TODO.txt +5 -0
- data/config/racf.yml.sample +27 -0
- data/lib/racf.rb +78 -0
- data/lib/racf/client.rb +138 -0
- data/lib/racf/commands/abstract_command.rb +13 -0
- data/lib/racf/commands/listgrp.rb +189 -0
- data/lib/racf/commands/listgrp/group_members_parser.rb +104 -0
- data/lib/racf/commands/listuser.rb +112 -0
- data/lib/racf/commands/rlist.rb +208 -0
- data/lib/racf/commands/search.rb +31 -0
- data/lib/racf/pagers/cached_ispf_pager.rb +91 -0
- data/lib/racf/pagers/ispf_pager.rb +57 -0
- data/lib/racf/s3270.rb +136 -0
- data/lib/racf/session.rb +149 -0
- data/lib/racf/version.rb +3 -0
- data/racf.gemspec +16 -0
- data/script/ci +3 -0
- data/spec/fixtures/config/racf.yml +9 -0
- data/spec/fixtures/listgrp/multiple_groups_multiple_members.txt +26 -0
- data/spec/fixtures/listgrp/multiple_members.txt +10 -0
- data/spec/fixtures/listgrp/no_members.txt +4 -0
- data/spec/fixtures/listgrp/not_found_groups.txt +21 -0
- data/spec/fixtures/listgrp/one_group.txt +7 -0
- data/spec/fixtures/listgrp/one_group_with_members.txt +13 -0
- data/spec/fixtures/listgrp/one_member.txt +7 -0
- data/spec/fixtures/listuser/all_users.txt +45 -0
- data/spec/fixtures/listuser/just_users_not_found.txt +3 -0
- data/spec/fixtures/listuser/one_user.txt +47 -0
- data/spec/fixtures/listuser/some_not_found_users.txt +88 -0
- data/spec/fixtures/racf_cache_dump.yml +9 -0
- data/spec/fixtures/rlist/gims.txt +135 -0
- data/spec/fixtures/rlist/gims_with_no_tims.txt +135 -0
- data/spec/fixtures/rlist/gims_with_not_found.txt +89 -0
- data/spec/fixtures/rlist/just_one_not_found.txt +1 -0
- data/spec/fixtures/rlist/multiple_not_found.txt +3 -0
- data/spec/fixtures/rlist/rlist_success.txt +50 -0
- data/spec/fixtures/rlist/tims_without_users.txt +119 -0
- data/spec/fixtures/search/gims.txt +30 -0
- data/spec/fixtures/search/tims.txt +30 -0
- data/spec/fixtures/session/screen_with_bottom_menu.txt +31 -0
- data/spec/fixtures/session/screen_with_top_and_bottom_menu.txt +47 -0
- data/spec/fixtures/session/screen_with_top_menu.txt +29 -0
- data/spec/fixtures/session/screen_without_menu.txt +13 -0
- data/spec/racf/client_spec.rb +155 -0
- data/spec/racf/commands/listgrp/group_members_parser_spec.rb +82 -0
- data/spec/racf/commands/listgrp_spec.rb +303 -0
- data/spec/racf/commands/listuser_spec.rb +123 -0
- data/spec/racf/commands/rlist_spec.rb +257 -0
- data/spec/racf/commands/search_spec.rb +66 -0
- data/spec/racf/pagers/cached_ispf_pager_spec.rb +212 -0
- data/spec/racf/pagers/ispf_pager_spec.rb +59 -0
- data/spec/racf/session_spec.rb +114 -0
- data/spec/racf_spec.rb +106 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/helpers.rb +5 -0
- metadata +162 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'racf/commands/abstract_command'
|
2
|
+
|
3
|
+
require 'state_machine'
|
4
|
+
|
5
|
+
module Racf
|
6
|
+
module Commands
|
7
|
+
class Listgrp < AbstractCommand
|
8
|
+
class GroupMembersParser
|
9
|
+
attr_reader :members
|
10
|
+
|
11
|
+
state_machine :state, :initial => :nothing_processed do
|
12
|
+
state :nothing_processed, :headers_processed, :first_line_processed,
|
13
|
+
:second_line_processed, :no_members
|
14
|
+
|
15
|
+
state :member_processed, :no_members do
|
16
|
+
def parsed?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
state all - [:member_processed, :no_members] do
|
22
|
+
def parsed?
|
23
|
+
false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
event :process_first_line do
|
28
|
+
transition [:headers_processed, :member_processed] => :first_line_processed
|
29
|
+
end
|
30
|
+
|
31
|
+
event :process_second_line do
|
32
|
+
transition :first_line_processed => :second_line_processed
|
33
|
+
end
|
34
|
+
|
35
|
+
event :process_third_line do
|
36
|
+
transition :second_line_processed => :member_processed
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize
|
41
|
+
@current_line = nil
|
42
|
+
@members = {}
|
43
|
+
@current_member = nil
|
44
|
+
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse(line)
|
49
|
+
@current_line = line.strip
|
50
|
+
|
51
|
+
case state_name
|
52
|
+
when :nothing_processed
|
53
|
+
process_headers
|
54
|
+
when :headers_processed, :member_processed
|
55
|
+
process_first_line
|
56
|
+
when :first_line_processed
|
57
|
+
process_second_line
|
58
|
+
when :second_line_processed
|
59
|
+
process_third_line
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
private
|
65
|
+
def process_headers
|
66
|
+
members_header = /USER\(S\)=\s+ACCESS=\s+ACCESS COUNT=\s+UNIVERSAL ACCESS=/
|
67
|
+
|
68
|
+
if @current_line.match(members_header)
|
69
|
+
@state = "headers_processed"
|
70
|
+
elsif @current_line == "NO USERS"
|
71
|
+
@state = "no_members"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def process_first_line
|
76
|
+
member, access, access_count, universal_access = @current_line.split(' ')
|
77
|
+
@current_member = member
|
78
|
+
@members[@current_member] = {
|
79
|
+
:access => access,
|
80
|
+
:access_count => access_count,
|
81
|
+
:universal_access => universal_access
|
82
|
+
}
|
83
|
+
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
def process_second_line
|
88
|
+
connect_attributes = @current_line.match(/CONNECT ATTRIBUTES=(.*?)$/)[1]
|
89
|
+
@members[@current_member].merge!(:connect_attributes => connect_attributes)
|
90
|
+
|
91
|
+
super
|
92
|
+
end
|
93
|
+
|
94
|
+
def process_third_line
|
95
|
+
revoke_date, resume_date = @current_line.match(/REVOKE DATE=(.*?)\s+RESUME DATE=(.*?)$/)[1..-1]
|
96
|
+
@members[@current_member].merge!(:revoke_date => revoke_date)
|
97
|
+
@members[@current_member].merge!(:resume_date => resume_date)
|
98
|
+
|
99
|
+
super
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
require 'state_machine'
|
3
|
+
require 'racf/commands/abstract_command'
|
4
|
+
|
5
|
+
|
6
|
+
module Racf
|
7
|
+
module Commands
|
8
|
+
class Listuser < AbstractCommand
|
9
|
+
state_machine :state, :initial => :nothing_processed do
|
10
|
+
state :nothing_processed, :name_processed, :user_processed
|
11
|
+
|
12
|
+
event :process_name do
|
13
|
+
transition [:nothing_processed, :user_processed] => :name_processed
|
14
|
+
end
|
15
|
+
|
16
|
+
event :finish_user_parsing do
|
17
|
+
transition :name_processed => :user_processed
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(session)
|
22
|
+
@users = {}
|
23
|
+
@current_user = {}
|
24
|
+
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def run(*args)
|
29
|
+
users_ids = extract_users_ids(args)
|
30
|
+
command = "LISTUSER (#{users_ids})"
|
31
|
+
raw_output = @session.run_command(command)
|
32
|
+
|
33
|
+
@scanner = StringScanner.new(raw_output)
|
34
|
+
|
35
|
+
log_number_of_users(@scanner.rest)
|
36
|
+
|
37
|
+
@user_index = 1
|
38
|
+
while @scanner.scan(/(.*?)$\n/)
|
39
|
+
case state_name
|
40
|
+
when :nothing_processed, :user_processed
|
41
|
+
process_name
|
42
|
+
when :name_processed
|
43
|
+
finish_user_parsing
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
users = @users
|
48
|
+
clear_state
|
49
|
+
|
50
|
+
users
|
51
|
+
end
|
52
|
+
|
53
|
+
def extract_resources(arguments)
|
54
|
+
resources = arguments.first
|
55
|
+
|
56
|
+
case resources
|
57
|
+
when String
|
58
|
+
[resources]
|
59
|
+
when Array
|
60
|
+
resources
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
def extract_users_ids(arguments)
|
66
|
+
extract_resources(arguments).join(" ")
|
67
|
+
end
|
68
|
+
|
69
|
+
def log_number_of_users(users_output)
|
70
|
+
@number_of_users = users_output.scan(/USER=.*?NAME=.*?/).size
|
71
|
+
Racf.logger.info("Processing #{@number_of_users} users")
|
72
|
+
end
|
73
|
+
|
74
|
+
def current_line
|
75
|
+
@scanner.matched
|
76
|
+
end
|
77
|
+
|
78
|
+
def process_name
|
79
|
+
if matched_data = current_line.strip.match(/USER=(.*?)\s+NAME=(.*?)\s+OWNER/)
|
80
|
+
Racf.logger.info("Processing user #{@user_index} of #{@number_of_users} users")
|
81
|
+
|
82
|
+
@current_user = {}
|
83
|
+
@current_user[:name] = matched_data[2]
|
84
|
+
user_id = matched_data[1]
|
85
|
+
@users[user_id.to_sym] = @current_user
|
86
|
+
|
87
|
+
Racf.logger.info("\tProcessing user #{user_id}")
|
88
|
+
|
89
|
+
super
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def finish_user_parsing
|
94
|
+
while @scanner.scan(/(.*?)$\n/)
|
95
|
+
if current_line.strip =~ /USER=(.*?)\s+NAME=(.*?)\s+OWNER/
|
96
|
+
@scanner.unscan
|
97
|
+
break
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
@current_user = {}
|
102
|
+
@user_index += 1
|
103
|
+
super
|
104
|
+
end
|
105
|
+
|
106
|
+
def clear_state
|
107
|
+
@users = {}
|
108
|
+
@current_user = {}
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
require 'state_machine'
|
3
|
+
require 'racf/commands/abstract_command'
|
4
|
+
|
5
|
+
module Racf
|
6
|
+
module Commands
|
7
|
+
|
8
|
+
class Rlist < AbstractCommand
|
9
|
+
NAME_HEADER = /CLASS\s+NAME/
|
10
|
+
NO_USERS = /NO\s+USERS\s+IN\s+ACCESS\s+LIST/
|
11
|
+
OWNER_HEADER = /LEVEL\s+OWNER\s+UNIVERSAL\s+ACCESS\s+YOUR\s+ACCESS\s+WARNING/
|
12
|
+
CONDITIONAL_ACCESS_LIST_HEADER = /ID\s+ACCESS\s+ACCESS\s+COUNT\s+CLASS\s+ENTITY\s+NAME/
|
13
|
+
|
14
|
+
state_machine :state, :initial => :nothing_processed do
|
15
|
+
state :nothing_processed, :name_processed, :members_processed,
|
16
|
+
:owner_processed, :users_processed, :resource_processed
|
17
|
+
|
18
|
+
event :process_name do
|
19
|
+
transition [:nothing_processed, :resource_processed] => :name_processed
|
20
|
+
end
|
21
|
+
|
22
|
+
event :process_members do
|
23
|
+
transition :name_processed => :members_processed
|
24
|
+
end
|
25
|
+
|
26
|
+
event :process_owner do
|
27
|
+
transition [:name_processed, :members_processed] => :owner_processed
|
28
|
+
end
|
29
|
+
|
30
|
+
event :process_users do
|
31
|
+
transition :owner_processed => :users_processed
|
32
|
+
end
|
33
|
+
|
34
|
+
event :finish_resource_processing do
|
35
|
+
transition :users_processed => :resource_processed
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize(session)
|
40
|
+
@resources = {}
|
41
|
+
@current_resource = {}
|
42
|
+
|
43
|
+
super
|
44
|
+
end
|
45
|
+
|
46
|
+
def run(*args)
|
47
|
+
class_name = args[0]
|
48
|
+
resources_names = extract_resources_names(args)
|
49
|
+
command = "RLIST #{class_name} (#{resources_names}) AUTHUSER"
|
50
|
+
|
51
|
+
raw_output = @session.run_command(command)
|
52
|
+
|
53
|
+
@scanner = StringScanner.new(raw_output)
|
54
|
+
log_number_of_resources(args[1])
|
55
|
+
|
56
|
+
while @scanner.scan(/(.*?)$\n/)
|
57
|
+
case state_name
|
58
|
+
when :nothing_processed, :resource_processed
|
59
|
+
process_name
|
60
|
+
when :name_processed
|
61
|
+
case class_name
|
62
|
+
when "TIMS"
|
63
|
+
process_owner
|
64
|
+
when "GIMS"
|
65
|
+
process_members
|
66
|
+
end
|
67
|
+
when :members_processed
|
68
|
+
process_owner
|
69
|
+
when :owner_processed
|
70
|
+
process_users
|
71
|
+
when :users_processed
|
72
|
+
finish_resource_processing
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
resources = @resources
|
77
|
+
clear_state
|
78
|
+
|
79
|
+
resources
|
80
|
+
end
|
81
|
+
|
82
|
+
def extract_resources(args)
|
83
|
+
resources = args[1]
|
84
|
+
|
85
|
+
case resources
|
86
|
+
when String
|
87
|
+
[resources]
|
88
|
+
when Array
|
89
|
+
resources
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
def extract_resources_names(arguments)
|
95
|
+
extract_resources(arguments).join(" ")
|
96
|
+
end
|
97
|
+
|
98
|
+
def log_number_of_resources(resources)
|
99
|
+
case resources
|
100
|
+
when String
|
101
|
+
Racf.logger.info("Processing #{resources} resources")
|
102
|
+
when Array
|
103
|
+
Racf.logger.info("Processing #{resources.size} resources")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def current_line
|
108
|
+
@scanner.matched
|
109
|
+
end
|
110
|
+
|
111
|
+
def skip_lines(number_of_lines)
|
112
|
+
number_of_lines.times { @scanner.scan(/(.*?)$\n/) }
|
113
|
+
end
|
114
|
+
|
115
|
+
def process_name
|
116
|
+
if current_line.strip.match(NAME_HEADER)
|
117
|
+
skip_lines(2)
|
118
|
+
matched_data = current_line.strip.match(/.*?\s+(.*?)$/)
|
119
|
+
|
120
|
+
resource_name = matched_data[1]
|
121
|
+
@resources.merge!({ resource_name.to_sym => @current_resource })
|
122
|
+
|
123
|
+
super
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def process_members
|
128
|
+
@current_resource[:tims] = []
|
129
|
+
|
130
|
+
if current_line.strip.match(/RESOURCES\s+IN\s+GROUP/)
|
131
|
+
skip_lines(1)
|
132
|
+
|
133
|
+
while @scanner.scan(/(.*?)$\n/)
|
134
|
+
break if current_line.strip.match(OWNER_HEADER) || current_line.strip.match(/NONE/)
|
135
|
+
|
136
|
+
matched_data = current_line.strip.match(/^(.*)$/)
|
137
|
+
@current_resource[:tims] << matched_data[1]
|
138
|
+
end
|
139
|
+
|
140
|
+
@scanner.unscan
|
141
|
+
|
142
|
+
@current_resource[:tims].map!(&:strip)
|
143
|
+
@current_resource[:tims] = @current_resource[:tims].find_all { |tim| !tim.empty? }
|
144
|
+
super
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def process_owner
|
149
|
+
if current_line.strip.match(OWNER_HEADER)
|
150
|
+
skip_lines(2)
|
151
|
+
matched_data = current_line.strip.match(/.*?\s+(.*?)\s+.*$/)
|
152
|
+
@current_resource[:owner] = matched_data[1]
|
153
|
+
|
154
|
+
super
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def process_users
|
159
|
+
@current_resource[:standard_access_list] = []
|
160
|
+
|
161
|
+
if current_line.strip.match(/USER\s+ACCESS\s+ACCESS\s+COUNT/)
|
162
|
+
skip_lines(1)
|
163
|
+
process_access_list
|
164
|
+
|
165
|
+
super
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def process_access_list
|
170
|
+
while @scanner.scan(/(.*?)$\n/)
|
171
|
+
break if current_line.strip.empty? ||
|
172
|
+
current_line.strip.match(NO_USERS) ||
|
173
|
+
current_line.strip.match(CONDITIONAL_ACCESS_LIST_HEADER)
|
174
|
+
|
175
|
+
matched_data = current_line.strip.match(/(.*?)\s+(.*?)\s+.*?$/)
|
176
|
+
user = {
|
177
|
+
:user_or_group => matched_data[1],
|
178
|
+
:authority_level => matched_data[2]
|
179
|
+
}
|
180
|
+
|
181
|
+
@current_resource[:standard_access_list] << user
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def finish_resource_processing
|
186
|
+
while @scanner.scan(/(.*?)$\n/)
|
187
|
+
if current_line.nil?
|
188
|
+
break
|
189
|
+
elsif current_line.strip.match(NAME_HEADER)
|
190
|
+
@scanner.unscan
|
191
|
+
break
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
@current_resource = {}
|
196
|
+
|
197
|
+
super
|
198
|
+
end
|
199
|
+
|
200
|
+
def clear_state
|
201
|
+
@resources = {}
|
202
|
+
@current_resource = {}
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'racf/commands/abstract_command'
|
2
|
+
|
3
|
+
module Racf
|
4
|
+
module Commands
|
5
|
+
class Search < AbstractCommand
|
6
|
+
# ATENTION: That method deals only with the SEARCH CLASS(TIMS)
|
7
|
+
# and SEARCH CLASS(GIMS) queries
|
8
|
+
#
|
9
|
+
def run(*args)
|
10
|
+
begin
|
11
|
+
command = "SEARCH #{args.first}"
|
12
|
+
response = @session.run_command(command)
|
13
|
+
|
14
|
+
profiles = []
|
15
|
+
response.each_line.each do |line|
|
16
|
+
profiles << line.strip
|
17
|
+
end
|
18
|
+
|
19
|
+
return profiles
|
20
|
+
rescue StandardError => e
|
21
|
+
error_traces = "\t" + e.message + "\n\t" + e.backtrace.join("\n\t")
|
22
|
+
Racf.logger.info("Ooops, an exception was raised!\n#{error_traces}")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def extract_resources(arguments)
|
27
|
+
[]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|