stratagem 0.2.3 → 0.2.4

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.
Files changed (65) hide show
  1. data/Manifest +16 -6
  2. data/Rakefile +8 -1
  3. data/lib/generators/stratagem/install/install_base.rb +13 -3
  4. data/lib/generators/stratagem/install/install_generator.rb +1 -1
  5. data/lib/stratagem.rb +42 -18
  6. data/lib/stratagem/authentication.rb +2 -5
  7. data/lib/stratagem/auto_mock.rb +1 -0
  8. data/lib/stratagem/auto_mock/aquifer.rb +49 -26
  9. data/lib/stratagem/auto_mock/factory.rb +1 -6
  10. data/lib/stratagem/auto_mock/user_loader.rb +38 -0
  11. data/lib/stratagem/client.rb +15 -4
  12. data/lib/stratagem/configuration/auth_auth.rb +19 -0
  13. data/lib/stratagem/configuration/core.rb +20 -0
  14. data/lib/stratagem/crawler/authentication.rb +17 -12
  15. data/lib/stratagem/crawler/authentication/automated.rb +40 -0
  16. data/lib/stratagem/crawler/authentication/base.rb +140 -0
  17. data/lib/stratagem/crawler/authentication/configured.rb +27 -0
  18. data/lib/stratagem/crawler/parameter_resolver.rb +12 -8
  19. data/lib/stratagem/crawler/route_invoker.rb +10 -13
  20. data/lib/stratagem/crawler/session.rb +14 -2
  21. data/lib/stratagem/crawler/site_model.rb +4 -173
  22. data/lib/stratagem/crawler/site_model/edge.rb +20 -0
  23. data/lib/stratagem/crawler/site_model/page.rb +121 -0
  24. data/lib/stratagem/crawler/site_model/page_set.rb +58 -0
  25. data/lib/stratagem/instrumentation/models.rb +3 -14
  26. data/lib/stratagem/instrumentation/models/annotations.rb +39 -5
  27. data/lib/stratagem/instrumentation/models/authentication.rb +0 -1
  28. data/lib/stratagem/instrumentation/models/authentication/authlogic/detect.rb +1 -0
  29. data/lib/stratagem/instrumentation/models/authentication/devise/detect.rb +1 -1
  30. data/lib/stratagem/instrumentation/models/authentication/devise/instrumentation.rb +0 -4
  31. data/lib/stratagem/instrumentation/models/metadata.rb +23 -1
  32. data/lib/stratagem/instrumentation/models/persistence.rb +3 -4
  33. data/lib/stratagem/instrumentation/models/persistence/active_record/metadata.rb +2 -2
  34. data/lib/stratagem/interface/browser.rb +9 -3
  35. data/lib/stratagem/interface/public/javascripts/stratagem.js +14 -12
  36. data/lib/stratagem/interface/views/index.haml +3 -3
  37. data/lib/stratagem/logger.rb +28 -2
  38. data/lib/stratagem/model.rb +6 -0
  39. data/lib/stratagem/model/application.rb +21 -134
  40. data/lib/stratagem/model/components/base.rb +1 -4
  41. data/lib/stratagem/model/components/controller.rb +1 -2
  42. data/lib/stratagem/model/components/model.rb +15 -15
  43. data/lib/stratagem/model/components/route.rb +3 -2
  44. data/lib/stratagem/model/components/view.rb +0 -1
  45. data/lib/stratagem/model/containers/base.rb +60 -0
  46. data/lib/stratagem/model/containers/gem.rb +25 -0
  47. data/lib/stratagem/model/containers/plugin.rb +11 -0
  48. data/lib/stratagem/model/containers/route.rb +19 -0
  49. data/lib/stratagem/model/parse_util.rb +3 -3
  50. data/lib/stratagem/model_builder.rb +1 -4
  51. data/lib/stratagem/rack_hack.rb +15 -0
  52. data/lib/stratagem/site_crawler.rb +5 -4
  53. data/lib/stratagem/snapshot.rb +5 -7
  54. data/spec/stratagem/configuration_spec.rb +32 -0
  55. data/stratagem.gemspec +5 -8
  56. data/templates/install/environments/stratagem.rb.erb +31 -2
  57. data/templates/install/script/stratagem +16 -0
  58. data/templates/install/tasks/stratagem.rake +2 -2
  59. metadata +36 -65
  60. data/bin/stratagem +0 -58
  61. data/lib/stratagem/scan.rb +0 -19
  62. data/lib/stratagem/scan/checks/email_address.rb +0 -15
  63. data/lib/stratagem/scan/checks/error_pages.rb +0 -25
  64. data/lib/stratagem/scan/result.rb +0 -45
  65. data/lib/stratagem/scanner.rb +0 -32
@@ -0,0 +1,38 @@
1
+ # Locates an instance of a user from configured credentials
2
+
3
+ module Stratagem::AutoMock
4
+ module UserLoader
5
+ def load_user_from_configuration(credentials)
6
+ possible_matches = []
7
+ auth_creds = credentials.authentication_parameters
8
+ model = credentials.model.constantize
9
+ @all_user_objects ||= model.all
10
+ log "\tloaded #{@all_user_objects.size} users from the database"
11
+ @all_user_objects.each do |user|
12
+ matching_attributes = matching_credentials(auth_creds, user)
13
+ possible_matches << [user, matching_attributes] if matching_attributes > 0
14
+ end
15
+
16
+ match = possible_matches.sort {|a,b| a[1] <=> b[1] }.last
17
+ if (match)
18
+ user = match.first
19
+ auth_creds.each {|k,v| user.stratagem.mock_attributes[k] = v }
20
+ user
21
+ else
22
+ nil
23
+ end
24
+ end
25
+
26
+ def matching_credentials(authentication_parameters, user)
27
+ count = 0
28
+ authentication_parameters.each do |attribute, value|
29
+ begin
30
+ count += 1 if (value == user.send(attribute.to_sym))
31
+ rescue
32
+ end
33
+ end
34
+ count
35
+ end
36
+
37
+ end
38
+ end
@@ -1,4 +1,6 @@
1
1
  require 'uri'
2
+ require 'zlib'
3
+ require 'base64'
2
4
 
3
5
  module Stratagem
4
6
  class Client
@@ -7,18 +9,29 @@ module Stratagem
7
9
  end
8
10
 
9
11
  def send(snapshot)
12
+ Stratagem.logger.phase('exporting')
10
13
  Stratagem.logger.debug "Sending report to server"
14
+
11
15
  url = URI.parse("#{@authentication.base_url}/snapshots")
12
16
  req = Stratagem.ssl? ? Net::HTTPS::Post.new(url.path) : Net::HTTP::Post.new(url.path)
17
+ model = snapshot.model.export.to_json
18
+ puts "Size: #{model.size}"
19
+ compressed = Zlib::Deflate.deflate(model, Zlib::BEST_COMPRESSION)
20
+ base64 = Base64.encode64(compressed)
21
+ puts "Compressed size: #{compressed.size}"
22
+ puts "Compressed size base 64: #{base64.size}"
13
23
 
14
24
  req.set_form_data({
15
25
  'auth_token' => @authentication.credentials[:token],
16
26
  'project_id' => @authentication.credentials[:project],
17
27
  'timestamp' => snapshot.timestamp.to_i,
18
- 'model' => snapshot.model.export.to_json
28
+ 'model' => model
19
29
  }, ';')
20
30
  client = Stratagem.ssl? ? Net::HTTPS.new(url.host, url.port) : Net::HTTP.new(url.host, url.port)
21
- res = client.start {|http| http.request(req) }
31
+ res = client.start {|http|
32
+ http.read_timeout= 360
33
+ http.request(req)
34
+ }
22
35
  puts "response:"
23
36
  case res
24
37
  when Net::HTTPSuccess, Net::HTTPRedirection
@@ -27,8 +40,6 @@ module Stratagem
27
40
  else
28
41
  res.error!
29
42
  end
30
-
31
- Stratagem.logger.phase('complete')
32
43
  end
33
44
  end
34
45
  end
@@ -0,0 +1,19 @@
1
+ module Stratagem
2
+ module Configuration
3
+ class AuthAuth
4
+ attr_accessor :model, :authorized_to_view_others_data, :authorized_to_modify_others_data, :authorized_to_upload_files, :authentication_parameters
5
+
6
+ def initialize
7
+ self.authorized_to_view_others_data = true
8
+ self.authorized_to_modify_others_data = false
9
+ self.authorized_to_upload_files = true
10
+ self.model = 'User'
11
+ end
12
+
13
+ def credentials(authentication_parameters)
14
+ self.authentication_parameters = authentication_parameters
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ module Stratagem
2
+ module Configuration
3
+ class Core
4
+ attr_accessor :use_transactional_crawling, :use_automatic_mocking
5
+ attr_reader :credentials
6
+
7
+ def initialize
8
+ @use_automatic_mocking = false
9
+ @use_transactional_crawling = true
10
+ @credentials = []
11
+ end
12
+
13
+ def authenticate_with(&block)
14
+ user = AuthAuth.new
15
+ block.call(user)
16
+ @credentials << user
17
+ end
18
+ end
19
+ end
20
+ end
@@ -7,20 +7,25 @@ module Stratagem::Crawler
7
7
  module Authentication
8
8
  include Stratagem::Crawler::TraceUtils
9
9
 
10
- def users
10
+ def user_model
11
+ model = nil
11
12
  page = find_login_form
12
- users = []
13
13
  if (page)
14
14
  form = page.login_form
15
15
  attr_names = form.inputs.map {|input| input.guess_attribute.to_sym }
16
16
  model = guess_login_model(attr_names)
17
- if (model)
18
- users = aquifer.instances_of(model.klass)
19
- else
20
- log "ERROR: Unable to determine user model"
21
- end
17
+ end
18
+ log "Authenticating with model #{model.klass.name}"
19
+ model
20
+ end
21
+
22
+ def users
23
+ users = []
24
+ model = user_model()
25
+ if (model)
26
+ users = aquifer.instances_of(model.klass).select {|user| user.stratagem.mock_attributes.size > 0 }
22
27
  else
23
- log "ERROR: Could not find login form"
28
+ log "ERROR: Unable to determine authentication model and / or form"
24
29
  end
25
30
  users
26
31
  end
@@ -54,8 +59,7 @@ module Stratagem::Crawler
54
59
  authentication.success = authentication.response_page.login_form.nil?
55
60
  end
56
61
  rescue
57
- puts $!.message
58
- puts $!.backtrace
62
+ Stratagem.logger.error($!)
59
63
  end
60
64
 
61
65
  puts "authenticated? #{authentication.success}"
@@ -80,12 +84,13 @@ module Stratagem::Crawler
80
84
 
81
85
  # if the first page has one or more redirects to it then use it; otherwise select with page with the fewest inputs
82
86
  if (possibilities.first.inbound_edges(:redirect).size > 0)
83
- return possibilities.first
87
+ authentication.login_page = possibilities.first
88
+ return authentication.login_page
84
89
  else
85
90
  page = possibilities.sort {|a,b| a.login_form.inputs.size <=> b.login_form.inputs.size }.first
86
91
  if (page)
87
92
  authentication.login_page = page
88
- return page
93
+ return authentication.login_page
89
94
  end
90
95
  end
91
96
  else
@@ -0,0 +1,40 @@
1
+ module Stratagem::Crawler::Authentication
2
+ class AuthenticationData
3
+ attr_accessor :success, :login_page, :form, :response_page, :ssl, :authenticated_with
4
+ end
5
+
6
+ class Base
7
+ include Stratagem::Crawler::TraceUtils
8
+
9
+ def user_models
10
+ model = nil
11
+ page = find_login_form
12
+ if (page)
13
+ form = page.login_form
14
+ attr_names = form.inputs.map {|input| input.guess_attribute.to_sym }
15
+ model = guess_login_model(attr_names)
16
+ end
17
+ log "Authenticating with model #{model.klass.name}"
18
+ [model]
19
+ end
20
+
21
+ def guess_login_model(attr_names)
22
+ selections = application_model.models.select {|model|
23
+ puts "#{model.klass.name} - #{model.model_attributes.keys.inspect}"
24
+ intersect = (model.model_attributes.keys & attr_names)
25
+ intersect.size > 0
26
+ }.sort {|a,b|
27
+ a_intersect = (a.model_attributes.keys & attr_names)
28
+ b_intersect = (b.model_attributes.keys & attr_names)
29
+ b_intersect.size <=> a_intersect.size
30
+ }
31
+
32
+ explicit_model = application_model.models.find {|model| model.klass.name == 'User' }
33
+ selections.unshift explicit_model if explicit_model
34
+
35
+ puts "selecting model #{selections.first.klass.name} for authentication" if (selections.size > 0)
36
+ selections.first
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,140 @@
1
+ module Stratagem::Crawler::Authentication
2
+ class AuthenticationData
3
+ attr_accessor :success, :login_page, :form, :response_page, :ssl, :authenticated_with
4
+ end
5
+
6
+ class Base
7
+ include Stratagem::Crawler::TraceUtils
8
+
9
+ def users
10
+ users = []
11
+ user_models.each do |user_model|
12
+ users += aquifer.instances_of(user_model.klass).select {|user| user.stratagem.mock_attributes.size > 0 }
13
+ end
14
+ users
15
+ end
16
+
17
+ def reset_authentication
18
+ @authentication_data = nil
19
+ end
20
+
21
+ def authentication
22
+ unless @authentication_data
23
+ @authentication_data = AuthenticationData.new()
24
+ site_model.authentication = @authentication_data
25
+ end
26
+ @authentication_data
27
+ end
28
+
29
+ def authenticate(user, recursion_count=0)
30
+ reset_authentication
31
+
32
+ login(user)
33
+ route = application_model.routes.recognize(request.path, :post)
34
+
35
+ redirected_to = nil
36
+ page = site_model.add(route, controller, request, response) {|redirect_url| redirected_to = redirect_url }
37
+ authentication.response_page = page
38
+
39
+ begin
40
+ if (request.url == (redirected_to || '')) || (![200,302].include?(response.code.to_i))
41
+ authentication.success = false
42
+ else
43
+ authentication.success = authentication.response_page.login_form.nil?
44
+ end
45
+ rescue
46
+ Stratagem.logger.error($!)
47
+ end
48
+
49
+ puts "authenticated? #{authentication.success}"
50
+ if (response && authentication.success)
51
+ authentication.ssl = request.ssl?
52
+ authentication.authenticated_with = user
53
+ yield
54
+ logout
55
+ else
56
+ puts response.body
57
+ false
58
+ end
59
+ end
60
+
61
+ def find_login_form
62
+ puts "finding login form"
63
+ if authentication.login_page.nil?
64
+ puts "locating login page"
65
+ puts "testing #{site_models.first.pages.size} pages"
66
+ possibilities = site_models.first.pages.select {|page| page.login_form != nil }
67
+ possibilities.sort! {|a,b| b.inbound_edges(:redirect).size <=> a.inbound_edges(:redirect).size }
68
+
69
+ # if the first page has one or more redirects to it then use it; otherwise select with page with the fewest inputs
70
+ if (possibilities.first.inbound_edges(:redirect).size > 0)
71
+ authentication.login_page = possibilities.first
72
+ return authentication.login_page
73
+ else
74
+ page = possibilities.sort {|a,b| a.login_form.inputs.size <=> b.login_form.inputs.size }.first
75
+ if (page)
76
+ authentication.login_page = page
77
+ return authentication.login_page
78
+ end
79
+ end
80
+ else
81
+ return authentication.login_page
82
+ end
83
+ nil
84
+ end
85
+
86
+ def logout
87
+ reset!
88
+ end
89
+
90
+ def login(user)
91
+ populate_login_form(user).submit {|action,params|
92
+ post(action, params)
93
+ }
94
+ end
95
+
96
+ def populate_login_form(user)
97
+ # set up the form
98
+ page = find_login_form
99
+ page.reload {|url| get url; [request,response] }
100
+ form = page.login_form
101
+
102
+ # map the input values
103
+ form.inputs.each do |input|
104
+ # try the expected
105
+ attribute_name = input.guess_attribute.to_sym
106
+ attribute_value = user.stratagem.read_mock_attribute(attribute_name) || input.value
107
+
108
+ # try again
109
+ if (attribute_value.nil? || attribute_value == '')
110
+ attribute_name = input.guess_alternate_attribute.to_sym
111
+ attribute_value = user.stratagem.read_mock_attribute(attribute_name) || input.value
112
+ end
113
+
114
+ # if it still failed is it a confirmation value? if so, try the original
115
+ if (attribute_value.nil? || attribute_value == '')
116
+ if (attribute_name.to_s =~ /confirm/)
117
+ possible_match = attribute_name.to_s.split('_').select {|a| a !~ /confirm/ }.join('_')
118
+ if user.stratagem.mock_attributes.keys.include?(possible_match)
119
+ attribute_value = user.stratagem.read_mock_attribute(possible_match) || input.value
120
+ end
121
+ end
122
+ end
123
+
124
+
125
+ if (input.kind_of? Stratagem::Crawler::Toggle)
126
+ input.check
127
+ elsif (user.stratagem.mock_attributes.keys.include?(attribute_name))
128
+ input.value = user.stratagem.read_mock_attribute(attribute_name) unless input.hidden?
129
+ elsif (attribute_name.to_s == 'authenticity_token')
130
+ puts input.value
131
+ else
132
+ puts user.stratagem.mock_attributes.inspect
133
+ puts "ERROR: Cannot find attribute #{attribute_name} in model #{user.class.name}"
134
+ end
135
+ end
136
+ form
137
+ end
138
+
139
+ end
140
+ end
@@ -0,0 +1,27 @@
1
+ module Stratagem::Crawler::Authentication
2
+ class Configured < Base
3
+ include Stratagem::AutoMock::UserLoader
4
+
5
+ def user_models
6
+ credentials.map {|user_credentials|
7
+ application_model.models.find {|model| model.klass.name == user_credentials.model }
8
+ }.compact
9
+ end
10
+
11
+ def users
12
+ log "Loading users for #{credentials.size} sets of credentials"
13
+ credentials.each do |user_credentials|
14
+ user = load_user_from_configuration(user_credentials)
15
+ if (user)
16
+ log "Loaded credentials for user #{user.id}: #{user.stratagem.mock_attributes.inspect}"
17
+ user.stratagem.related_objects.each do |object|
18
+ (objects_by_class[object.class] ||= []) << object
19
+ end
20
+ end
21
+ end
22
+
23
+ log "Loaded #{objects_by_class.keys.size} object types: #{objects_by_class.keys.inspect}"
24
+
25
+ end
26
+ end
27
+ end
@@ -6,12 +6,13 @@ module Stratagem::Crawler
6
6
  route_infos, params = build_url(route_container, resolved_params)
7
7
  route_info = route_infos.first
8
8
  unknown_params = params.keys
9
+ puts "resolving parameter types - #{unknown_params.inspect}"
9
10
 
10
11
  if (unknown_params.size > 0)
11
12
  resolve_with_convention(unknown_params, resolved_params)
12
13
  resolve_with_instrumentation(route_container, resolved_params)
13
14
  resolve_id_with_convention(route_container, unknown_params, resolved_params)
14
- log "\tresolved parameter types - #{resolved_params.inspect}"
15
+ log "\tresolved parameter types - #{resolved_params.keys.inspect}, unknown - #{unknown_params.inspect}"
15
16
  end
16
17
 
17
18
  if (resolved_params.size > 0)
@@ -43,7 +44,7 @@ module Stratagem::Crawler
43
44
  # TODO inspect is a hack, refactor
44
45
  if (invocation.args.include?(value.first)) || (invocation.args.inspect.include?('"'+value.first.to_s+'"'))
45
46
  # found match
46
- puts "\t\tresolved #{key} to #{invocation.model_class}"
47
+ puts "\t\tresolved #{key} to #{invocation.model_class} using instrumentation"
47
48
  unknown_params.delete(key)
48
49
  resolved_params[key] = invocation.model_class
49
50
  progress += 1
@@ -58,15 +59,18 @@ module Stratagem::Crawler
58
59
  end
59
60
 
60
61
  def resolve_with_convention(unknown_params, resolved_params)
61
- unknown_params.each do |param|
62
- if (param =~ /_id$/)
62
+ unknown_params.clone.each do |param|
63
+ if (param.to_s =~ /_id$/)
63
64
  begin
64
- model = param.gsub(/^:/,'').gsub(/_id$/, '').camelize.constantize
65
+ puts "\t\tattempting to resolve #{param.sub(/^:/,'').sub(/_id$/, '').camelize}"
66
+ model = param.sub(/^:/,'').sub(/_id$/, '').camelize.constantize
65
67
  #success
66
68
  resolved_params[param] = model
67
69
  unknown_params.delete(param)
68
- puts "\t\tresolved #{param} to #{model} using convention"
70
+ puts "\t\tresolved #{param} to #{model.name} using convention"
69
71
  rescue NameError
72
+ puts "NAMEERROR: #{$!.message}"
73
+ rescue
70
74
  puts "ERROR: #{$!.message}"
71
75
  end
72
76
  end
@@ -77,12 +81,12 @@ module Stratagem::Crawler
77
81
  def resolve_id_with_convention(route_container, unknown_params, resolved_params)
78
82
  if (unknown_params.include?('id'))
79
83
  route_container.path.split('/').inject(nil) {|prev,part|
80
- if (prev && part == ':id')
84
+ if (prev && part =~ /^\:id/)
81
85
  begin
82
86
  resolved_params['id'] = prev.singularize.camelize.constantize
83
87
  unknown_params.delete('id')
84
88
  rescue
85
- puts $!.message
89
+ Stratagem.logger.error($!)
86
90
  end
87
91
  end
88
92
  part