siffer 0.0.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 (91) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +65 -0
  3. data/Rakefile +64 -0
  4. data/bin/siffer +71 -0
  5. data/doc/SIF ImplementationSpecification.pdf +0 -0
  6. data/doc/rdoc/classes/Siffer.html +374 -0
  7. data/doc/rdoc/classes/Siffer/Agent.html +296 -0
  8. data/doc/rdoc/classes/Siffer/Container.html +286 -0
  9. data/doc/rdoc/classes/Siffer/Messages.html +141 -0
  10. data/doc/rdoc/classes/Siffer/Messages/Ack.html +216 -0
  11. data/doc/rdoc/classes/Siffer/Messages/Acl.html +160 -0
  12. data/doc/rdoc/classes/Siffer/Messages/Error.html +248 -0
  13. data/doc/rdoc/classes/Siffer/Messages/Message.html +359 -0
  14. data/doc/rdoc/classes/Siffer/Messages/Message/Header.html +181 -0
  15. data/doc/rdoc/classes/Siffer/Messages/Register.html +257 -0
  16. data/doc/rdoc/classes/Siffer/Messages/RequestBody.html +300 -0
  17. data/doc/rdoc/classes/Siffer/Messages/Status.html +269 -0
  18. data/doc/rdoc/classes/Siffer/Messaging.html +331 -0
  19. data/doc/rdoc/classes/Siffer/Protocol.html +388 -0
  20. data/doc/rdoc/classes/Siffer/Protocol/NonPostRequest.html +111 -0
  21. data/doc/rdoc/classes/Siffer/Protocol/UnknownPath.html +111 -0
  22. data/doc/rdoc/classes/Siffer/Registration.html +391 -0
  23. data/doc/rdoc/classes/Siffer/Request.html +209 -0
  24. data/doc/rdoc/classes/Siffer/RequestLogger.html +211 -0
  25. data/doc/rdoc/classes/Siffer/Response.html +182 -0
  26. data/doc/rdoc/classes/Siffer/Server.html +242 -0
  27. data/doc/rdoc/created.rid +1 -0
  28. data/doc/rdoc/files/LICENSE.html +129 -0
  29. data/doc/rdoc/files/README_rdoc.html +184 -0
  30. data/doc/rdoc/files/lib/agent_rb.html +101 -0
  31. data/doc/rdoc/files/lib/container_rb.html +108 -0
  32. data/doc/rdoc/files/lib/messages/ack_rb.html +101 -0
  33. data/doc/rdoc/files/lib/messages/acl_rb.html +101 -0
  34. data/doc/rdoc/files/lib/messages/error_rb.html +101 -0
  35. data/doc/rdoc/files/lib/messages/message_rb.html +101 -0
  36. data/doc/rdoc/files/lib/messages/register_rb.html +101 -0
  37. data/doc/rdoc/files/lib/messages/request_body_rb.html +101 -0
  38. data/doc/rdoc/files/lib/messages/status_rb.html +101 -0
  39. data/doc/rdoc/files/lib/messages_rb.html +114 -0
  40. data/doc/rdoc/files/lib/messaging_rb.html +101 -0
  41. data/doc/rdoc/files/lib/protocol_rb.html +101 -0
  42. data/doc/rdoc/files/lib/registration_rb.html +101 -0
  43. data/doc/rdoc/files/lib/request_logger_rb.html +101 -0
  44. data/doc/rdoc/files/lib/request_rb.html +101 -0
  45. data/doc/rdoc/files/lib/response_rb.html +101 -0
  46. data/doc/rdoc/files/lib/server_rb.html +101 -0
  47. data/doc/rdoc/files/lib/siffer_rb.html +115 -0
  48. data/doc/rdoc/fr_class_index.html +47 -0
  49. data/doc/rdoc/fr_file_index.html +46 -0
  50. data/doc/rdoc/fr_method_index.html +96 -0
  51. data/doc/rdoc/index.html +24 -0
  52. data/doc/rdoc/rdoc-style.css +208 -0
  53. data/lib/agent.rb +43 -0
  54. data/lib/container.rb +96 -0
  55. data/lib/messages.rb +7 -0
  56. data/lib/messages/ack.rb +43 -0
  57. data/lib/messages/acl.rb +25 -0
  58. data/lib/messages/error.rb +174 -0
  59. data/lib/messages/message.rb +71 -0
  60. data/lib/messages/register.rb +60 -0
  61. data/lib/messages/request_body.rb +66 -0
  62. data/lib/messages/status.rb +55 -0
  63. data/lib/messaging.rb +96 -0
  64. data/lib/protocol.rb +159 -0
  65. data/lib/registration.rb +87 -0
  66. data/lib/request.rb +25 -0
  67. data/lib/request_logger.rb +31 -0
  68. data/lib/response.rb +26 -0
  69. data/lib/server.rb +40 -0
  70. data/lib/siffer.rb +44 -0
  71. data/spec/agent_spec.rb +53 -0
  72. data/spec/cli_spec.rb +40 -0
  73. data/spec/container_spec.rb +103 -0
  74. data/spec/default_agent +6 -0
  75. data/spec/default_server +5 -0
  76. data/spec/message_specs/ack_spec.rb +28 -0
  77. data/spec/message_specs/error_spec.rb +24 -0
  78. data/spec/message_specs/header_spec.rb +25 -0
  79. data/spec/message_specs/message_spec.rb +57 -0
  80. data/spec/message_specs/register_spec.rb +86 -0
  81. data/spec/message_specs/request_body_spec.rb +58 -0
  82. data/spec/message_specs/status_spec.rb +25 -0
  83. data/spec/messaging_spec.rb +88 -0
  84. data/spec/protocol_spec.rb +49 -0
  85. data/spec/registration_spec.rb +33 -0
  86. data/spec/request_logger_spec.rb +15 -0
  87. data/spec/request_spec.rb +10 -0
  88. data/spec/response_spec.rb +24 -0
  89. data/spec/server_spec.rb +35 -0
  90. data/spec/spec_helper.rb +26 -0
  91. metadata +191 -0
@@ -0,0 +1,174 @@
1
+ module Siffer
2
+ module Messages
3
+ class Error
4
+
5
+ attr_reader :category, :code, :description
6
+
7
+ def initialize(category, code, desc = nil)
8
+ @category = CATEGORY[category]
9
+ @category_index = category
10
+ @code = CODE[@category][code]
11
+ @code_index = code
12
+ @description = desc
13
+ end
14
+
15
+ def read
16
+ xml = Builder::XmlMarkup.new
17
+ xml.SIF_Error { |error|
18
+ error.SIF_Category(@category_index)
19
+ error.SIF_Code(@code_index)
20
+ error.SIF_Desc(@code)
21
+ error.SIF_ExtendedDesc(@description) unless @description.nil?
22
+ }
23
+ end
24
+ alias :to_str :read
25
+
26
+
27
+ # Categories of Errors specified by SIF
28
+ CATEGORY = [
29
+ "Unknown (This should NEVER be used if possible)",
30
+ "XML Validation",
31
+ "Encryption",
32
+ "Authentication",
33
+ "Access and Permissions",
34
+ "Registration",
35
+ "Provision",
36
+ "Subscription",
37
+ "Request and Response",
38
+ "Event Reporting and Processing",
39
+ "Transport",
40
+ "System (OS, Database, Vendor localized, etc.)",
41
+ "Generic Message Handling",
42
+ "SMB Handling"
43
+ ]
44
+
45
+ # Codes related to CATEGORY
46
+ # Some codes are blank to match the SIF Spec
47
+ CODE = {
48
+ CATEGORY[1] => [ # Xml Validation
49
+ "",
50
+ "Generic error",
51
+ "Message is not well-formed",
52
+ "Generic validation error",
53
+ "Invalid value for element/attribute",
54
+ "",
55
+ "Missing mandatory element/attribute"
56
+ ],
57
+ CATEGORY[2] => [ # Encryption
58
+ "",
59
+ "Generic error"
60
+ ],
61
+ CATEGORY[3] => [ # Authentication
62
+ "",
63
+ "Generic error",
64
+ "Generic authentication error (with signature)",
65
+ "Missing sender's certificate",
66
+ "Invalid certificate",
67
+ "Sender's certificate is not trusted",
68
+ "Expired certificate",
69
+ "Invalid signature",
70
+ "Invalid encryption algorithm (only accepts MD4)",
71
+ "Missing public key of the receiver (when decrypting message)",
72
+ "Missing receiver's private key (when decrypting message)"
73
+ ],
74
+ CATEGORY[4] => [ # Access and Permissions
75
+ "",
76
+ "Generic error",
77
+ "No permission to register",
78
+ "No permission to provide this object",
79
+ "No permission to subscribe to this SIF_Event",
80
+ "No permission to request this object",
81
+ "No permission to respond to this object request",
82
+ "No permission to publish SIF_Event",
83
+ "No permission to administer policies",
84
+ "SIF_SourceId is not registered",
85
+ "No permission to publish SIF_Event Add",
86
+ "No permission to publish SIF_Event Change",
87
+ "No permission to publish SIF_Event Delete"
88
+ ],
89
+ CATEGORY[5] => [ # Registration
90
+ "",
91
+ "Generic error",
92
+ "The SIF_SourceId is invalid",
93
+ "Requested transport protocol is unsupported",
94
+ "Requested SIF_Version(s) not supported",
95
+ "",
96
+ "Requested SIF_MaxBufferSize is too small",
97
+ "ZIS requires a secure transport",
98
+ "",
99
+ "Agent is registered for push mode (returned when a push-mode agent sends a SIF_GetMessage)",
100
+ "ZIS does not support the requested Accept-Encoding value"
101
+ ],
102
+ CATEGORY[6] => [ # Provision
103
+ "",
104
+ "Generic error",
105
+ "",
106
+ "Invalid object",
107
+ "Object already has a provider (SIF_Provide message)"
108
+ ],
109
+ CATEGORY[7] => [ # Subscription
110
+ "",
111
+ "Generic error",
112
+ "",
113
+ "Invalid object"
114
+ ],
115
+ CATEGORY[8] => [ # Request and Response
116
+ "",
117
+ "Generic error",
118
+ "",
119
+ "Invalid object",
120
+ "No provider",
121
+ "",
122
+ "",
123
+ "Responder does not support requested SIF_Version",
124
+ "Responder does not support requested SIF_MaxBufferSize",
125
+ "Unsupported query in request",
126
+ "Invalid SIF_RequestMsgId specified in SIF_Response",
127
+ "SIF_Response is larger than requested SIF_MaxBufferSize",
128
+ "SIF_PacketNumber is invalid in SIF_Response",
129
+ "SIF_Response does not match any SIF_Version from SIF_Request",
130
+ "SIF_DestinationId does not match SIF_SourceId from SIF_Request",
131
+ "No support for SIF_ExtendedQuery",
132
+ "SIF_RequestMsgId deleted from cache due to timeout",
133
+ "SIF_RequestMsgId deleted from cache by administrator",
134
+ "SIF_Request cancelled by requesting agent"
135
+ ],
136
+ CATEGORY[9] => [ # Event Reporting and Processing
137
+ "",
138
+ "Generic error",
139
+ "",
140
+ "Invalid event"
141
+ ],
142
+ CATEGORY[10] => [ # Transport
143
+ "",
144
+ "Generic error",
145
+ "Requested protocol is not supported",
146
+ "Secure channel requested and no secure path exists",
147
+ "Unable to establish connection"
148
+ ],
149
+ CATEGORY[11] => [ # System
150
+ "",
151
+ "Generic Error"
152
+ ],
153
+ CATEGORY[12] => [ # Generic Message Handling
154
+ "",
155
+ "Generic error",
156
+ "Message not supported",
157
+ "Version not supported",
158
+ "Context not supported",
159
+ "Protocol error",
160
+ "No such message (as identified by SIF_OriginalMsgId)",
161
+ "Multiple contexts not supported"
162
+ ],
163
+ CATEGORY[13] => [ # SMB Handling
164
+ "",
165
+ "Generic error",
166
+ "SMB can only be invoked during a SIF_Event acknowledgement",
167
+ "Final SIF_Ack expected from Push-Mode Agent",
168
+ "Incorrect SIF_MsgId in final SIF_Ack"
169
+ ]
170
+ }
171
+
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,71 @@
1
+ module Siffer
2
+ module Messages
3
+
4
+ # Base class for all Messages in the framework. Embeds the XMLNS as
5
+ # well as the version of SIF implemented.
6
+ class Message
7
+
8
+ attr_reader :xmlns, :version, :header
9
+
10
+ # source parameter is required (string describing the original sender)
11
+ def initialize(source, options = {})
12
+ raise ArgumentError, "Source not provided." if source.nil?
13
+ @xmlns = options[:xmlns] || Siffer.sif_xmlns
14
+ @version = options[:version] || Siffer.sif_version
15
+ @header = Header.new(source)
16
+ @body = Builder::XmlMarkup.new
17
+ end
18
+
19
+ # Builds the xml for this message. Accepts a block if nested
20
+ # elements are required.
21
+ def content
22
+ @xml ||= @body.SIF_Message(:version => @version, :xmlns => @xmlns) { |xml|
23
+ yield xml if block_given?
24
+ }
25
+ end
26
+
27
+ # Alias method to the Header SourceId
28
+ def source_id() header.source_id; end
29
+ # Alias method to the Header MsgId
30
+ def msg_id() header.msg_id; end
31
+
32
+ # used to remove the stack too deep issue when overridden
33
+ # there has to be a better way .... TODO: Fix the alias crap
34
+ alias :body :content
35
+
36
+ def put_header_into(xml)
37
+ xml.SIF_Header { |head|
38
+ head.SIF_MsgId(@header.msg_id)
39
+ head.SIF_Timestamp(@header.timestamp)
40
+ head.SIF_SourceId(@header.source_id)
41
+ }
42
+ end
43
+
44
+ # Returns the content of the message
45
+ def read
46
+ # this construct prevents the xml from being built
47
+ # over and over again for every read made to this
48
+ # message.
49
+ @xml ||= content
50
+ end
51
+ alias :to_str :read
52
+
53
+ # Each Message requires a Header to identify the source.
54
+ # You shouldn't need to initialize this by itself, it is
55
+ # used by Message.
56
+ class Header
57
+
58
+ attr_reader :timestamp, :msg_id, :source_id
59
+
60
+ def initialize(source)
61
+ raise ArgumentError, "Source not provided." if source.nil?
62
+ @timestamp = Time.now
63
+ @msg_id = UUID.generate(:compact).upcase
64
+ @source_id = source
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,60 @@
1
+ module Siffer
2
+ module Messages
3
+
4
+ # Message used by Agents to register themselves with the ZIS.
5
+ class Register < Message
6
+
7
+ attr_reader :name, :version, :vendor, :max_buffer, :mode
8
+
9
+ def initialize(source, name, options = {})
10
+ super(source, options)
11
+ raise ArgumentError, "Agent name required" unless name
12
+ @name = name
13
+ @version = options[:version] || Siffer.sif_version
14
+ @max_buffer = options[:max_buffer] || 1024
15
+ @mode = options[:mode] || 'Pull'
16
+ @vendor = options[:vendor] || Siffer.vendor
17
+ end
18
+
19
+ def content
20
+ body do |reg|
21
+ reg.SIF_Register() { |xml|
22
+ put_header_into xml
23
+ reg.SIF_Name(@name)
24
+ reg.SIF_Version(@version)
25
+ reg.SIF_MaxBufferSize(@max_buffer)
26
+ reg.SIF_Mode(@mode)
27
+ }
28
+ end
29
+ end
30
+
31
+ # Parses the body passed as a Register message and returns a
32
+ # RequestBody that provides access to Register properties.
33
+ def self.parse(body)
34
+ RegisterBody.parse(body)
35
+ end
36
+
37
+ class RegisterBody < RequestBody #:nodoc:
38
+
39
+ def name
40
+ (@doc/:SIF_Name).text
41
+ end
42
+
43
+ def version
44
+ (@doc/:SIF_Version).text
45
+ end
46
+
47
+ def max_buffer
48
+ (@doc/:SIF_MaxBufferSize).text.to_i
49
+ end
50
+
51
+ def mode
52
+ (@doc/:SIF_Mode).text
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,66 @@
1
+ module Siffer
2
+
3
+ module Messages
4
+
5
+ # This class is used to parse the SIF Message information
6
+ # out of the Request Body. It parses enough information to redirect
7
+ # to other processes. It does not parse details of the Message such as
8
+ # Protocol if a Register message, or Error if an Ack message. Each
9
+ # message type will inherit this class in order to provide a #parse
10
+ # method that will allow access to those specific attributes.
11
+ # It provides access to:
12
+ # * source_id
13
+ # * msg_id
14
+ # * type
15
+ class RequestBody
16
+
17
+ def initialize(xml_string)
18
+ @xml = xml_string
19
+ @doc = Hpricot(@xml, :xml => true)
20
+ end
21
+
22
+ # Will return the name of the element found just inside the root
23
+ # or just inside SIF_SystemControl if present.
24
+ def type
25
+ begin
26
+ msg_type = (@doc/:SIF_Message).first.children[0].name
27
+ if msg_type == "SIF_SystemControl"
28
+ msg_type = (@doc/:SIF_Message/:SIF_SystemControl).first.children[0].name
29
+ end
30
+ drop_sif msg_type
31
+ rescue
32
+ raise "Failed to parse #{@xml} for SIF Type"
33
+ end
34
+ end
35
+
36
+ # The SIF_SourceId from the SIF_Header
37
+ def source_id
38
+ (@doc/"//SIF_Header/SIF_SourceId/").text
39
+ end
40
+
41
+ # The SIF_MsgId from the SIF_Header
42
+ def msg_id
43
+ (@doc/"//SIF_Header/SIF_MsgId/").text
44
+ end
45
+
46
+ # Parses the xml provided and returns a RequestBody.
47
+ # Xml must respond to read or be a String.
48
+ def self.parse(xml)
49
+ unless xml.respond_to?("read") or xml.is_a?(String)
50
+ raise ArgumentError, "Unable to read Xml"
51
+ end
52
+ xml = xml.read if xml.respond_to?("read")
53
+ self.new(xml) # use self because we are going to inherit this class
54
+ end
55
+
56
+ private
57
+ # Drops the SIF_ from the front of the string provided
58
+ def drop_sif(string)
59
+ string.gsub!("SIF_","")
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,55 @@
1
+ module Siffer
2
+ module Messages
3
+
4
+ class Status
5
+
6
+ attr_reader :code, :description, :data
7
+
8
+ def initialize(code,data)
9
+ @code = code
10
+ @description = CODES[code]
11
+ @data = data
12
+ end
13
+
14
+ def read
15
+ xml = Builder::XmlMarkup.new
16
+ xml.SIF_Status { |status|
17
+ status.SIF_Code(code)
18
+ status.SIF_Description(description)
19
+ if data.nil?
20
+ status.SIF_Data
21
+ else
22
+ status.SIF_Data{ |data_node| data_node << data.read }
23
+ end
24
+ }
25
+ end
26
+ alias :to_str :read
27
+
28
+ def self.method_missing(sym,*args)
29
+ data = args.first
30
+ status = nil
31
+ CODES.each do |key, val|
32
+ if val.downcase.to_sym == sym
33
+ status = Status.new(key,data)
34
+ end
35
+ end
36
+ if status
37
+ return status
38
+ else
39
+ super(sym,*args)
40
+ end
41
+ end
42
+
43
+ CODES = {
44
+ 0 => "Success",
45
+ 1 => "Immediate",
46
+ 2 => "Intermediate",
47
+ 3 => "Final",
48
+ 7 => "Duplicate",
49
+ 8 => "Sleeping",
50
+ 9 => "No Messages"
51
+ }
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,96 @@
1
+ module Siffer
2
+ # The Messaging module takes care of all of the Message handling for
3
+ # Siffer. It will check messages against content-type constraints as
4
+ # well as validate the message XML against constraints outlined in the
5
+ # SIF Implementation Specification.
6
+ module Messaging
7
+
8
+ include Siffer::Messages
9
+
10
+ class BadContentType < Exception #:nodoc:
11
+ end
12
+ class MalformedXml < Exception #:nodoc:
13
+ end
14
+ class MalformedSIFMessage < Exception #:nodoc:
15
+ end
16
+ class XmlNsMismatch < Exception #:nodoc:
17
+ end
18
+ class VersionMismatch < Exception #:nodoc:
19
+ end
20
+
21
+ # Checks the request against the messaging constraints:
22
+ # * check_content_type
23
+ # * validate_message
24
+ # Raises exceptions accordingly.
25
+ def request_failed_messaging?
26
+ begin
27
+ check_content_type
28
+ validate_message
29
+ rescue BadContentType
30
+ # Make a better Not Acceptable response
31
+ @response = Response.new(Siffer::Protocol::HTTP_STATUS_CODES[406],
32
+ 406,
33
+ {"Content-Type" => MIME_TYPES["htm"]})
34
+ rescue MalformedXml; error_response(1,2);
35
+ rescue MalformedSIFMessage; error_response(12,2);
36
+ rescue VersionMismatch; error_response(12,3);
37
+ rescue XmlNsMismatch; error_response(1,4,"XMLNS not compatible with SIF")
38
+ end
39
+ end
40
+
41
+ # Provides a context for the original message.
42
+ def using_message_from(request, &block)
43
+ yield if block_given?
44
+ end
45
+
46
+ # Reads request content type and validates it's either text/xml
47
+ # or application/xml;charset=utf-8. If it isn't it will raise a
48
+ # BadContentType exception.
49
+ def check_content_type
50
+ content_type = @request.content_type
51
+ unless [MIME_TYPES["xml"], MIME_TYPES["appxml"]].include? content_type
52
+ raise BadContentType
53
+ end
54
+ end
55
+
56
+ # Validates the Request Body against the constraints of SIF Messaging.
57
+ # The word "validate" is used loosely as there is no XML validation
58
+ # currently.
59
+ #
60
+ # Currently validates:
61
+ # * <tt>well-formed XML</tt> - XML must be well formed.
62
+ # * <tt>message root</tt> - XML must be a SIF message.
63
+ # * <tt>version</tt> - Must match SIF version implemented.
64
+ # * <tt>xmlns</tt> - Must match xmlns implemented.
65
+ #
66
+ # Does not validate:
67
+ # * valid XML
68
+ # * different versions of SIF
69
+ def validate_message
70
+ begin
71
+ body = (@request.body.respond_to? :read) ? @request.body.read : @request.body
72
+ xml = REXML::Document.new(body)
73
+ # validate Message Root
74
+ raise MalformedXml if xml.root.nil?
75
+ # validate Message Root is a SIF_Message
76
+ raise MalformedSIFMessage if xml.root.name != "SIF_Message"
77
+ # validate SIF version
78
+ raise VersionMismatch if xml.root.attributes["version"] != Siffer.sif_version
79
+ # validate SIF xmlns
80
+ raise XmlNsMismatch if xml.root.attributes["xmlns"] != Siffer.sif_xmlns
81
+ # any others?
82
+ rescue REXML::ParseException
83
+ raise MalformedXml
84
+ end
85
+ end
86
+
87
+ # MIME Types used in Siffer
88
+ MIME_TYPES = {
89
+ "appxml" => "application/xml;charset=utf-8",
90
+ "htm" => "text/html",
91
+ "html" => "text/html",
92
+ "xml" => "text/xml"
93
+ }
94
+
95
+ end
96
+ end