fractalpenguin-googlesso 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ require 'xmlcanonicalizer'
2
+ require 'processresponse'
3
+
4
+ class FractalPenguinGoogleSSO
5
+ VERSION = '0.0.1'
6
+ end
@@ -0,0 +1,37 @@
1
+ <samlp:Response ID="<RESPONSE_ID>" IssueInstant="<ISSUE_INSTANT>" Version="2.0"
2
+ xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
3
+ xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
4
+ xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
5
+ <samlp:Status>
6
+ <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
7
+ </samlp:Status>
8
+ <Assertion ID="<ASSERTION_ID>"
9
+ IssueInstant="2003-04-17T00:46:02Z" Version="2.0"
10
+ xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
11
+ <Issuer><ISSUER_DOMAIN></Issuer>
12
+ <Subject>
13
+ <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">
14
+ <USERNAME_STRING>
15
+ </NameID>
16
+ <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
17
+ <SubjectConfirmationData
18
+ Recipient="<DESTINATION>"
19
+ NotOnOrAfter="<NOT_ON_OR_AFTER>"
20
+ InResponseTo="<REQUEST_ID>"/>
21
+ </SubjectConfirmation>
22
+ </Subject>
23
+ <Conditions NotBefore="<NOT_BEFORE>"
24
+ NotOnOrAfter="<NOT_ON_OR_AFTER>">
25
+ <AudienceRestriction>
26
+ <Audience><DESTINATION></Audience>
27
+ </AudienceRestriction>
28
+ </Conditions>
29
+ <AuthnStatement AuthnInstant="<AUTHN_INSTANT>">
30
+ <AuthnContext>
31
+ <AuthnContextClassRef>
32
+ urn:oasis:names:tc:SAML:2.0:ac:classes:Password
33
+ </AuthnContextClassRef>
34
+ </AuthnContext>
35
+ </AuthnStatement>
36
+ </Assertion>
37
+ </samlp:Response>
@@ -0,0 +1,19 @@
1
+ <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
2
+ <SignedInfo>
3
+ <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
4
+ <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
5
+ <Reference URI="">
6
+ <Transforms>
7
+ <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
8
+ </Transforms>
9
+ <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
10
+ <DigestValue></DigestValue>
11
+ </Reference>
12
+ </SignedInfo>
13
+ <SignatureValue></SignatureValue>
14
+ <KeyInfo>
15
+ <X509Data>
16
+ <X509Certificate></X509Certificate>
17
+ </X509Data>
18
+ </KeyInfo>
19
+ </Signature>
@@ -0,0 +1,270 @@
1
+ require "openssl"
2
+ require "openssl/x509"
3
+ require "base64"
4
+ require "rexml/document"
5
+ require "rexml/xpath"
6
+ require "zlib"
7
+ require "digest/sha1"
8
+ require "xmlcanonicalizer"
9
+
10
+ include REXML
11
+ include OpenSSL
12
+
13
+ class XmlProcessResponse
14
+ attr_accessor :acs, :encodedresponse, :authenticateform, :logger
15
+
16
+ # Constructor @.@
17
+ #**********************************************************************************
18
+ def initialize(certificate_path, private_key_path, crypt_type = "rsa", issuer = "", issueinstant = "", providername = "", acs = "", relayState = "")
19
+ @certificate_path = certificate_path
20
+ @private_key_path = private_key_path
21
+ @crypt_type = crypt_type
22
+ @issuer = issuer
23
+ @issueinstant = issueinstant
24
+ @providername = providername
25
+ @acs = acs
26
+ @relayState = relayState
27
+ @requestID = ""
28
+
29
+ libpath = "#{Gem.dir}/gems/fractalpenguin-googlesso-#{FractalPenguinGoogleSSO::VERSION}/lib"
30
+ @signature_template_path = "#{libpath}/SignatureTemplate.xml"
31
+ @response_template_path = "#{libpath}/SamlResponseTemplate.xml"
32
+
33
+ @encodedresponse = ""
34
+ @authenticateform = ""
35
+
36
+ @logger = Logger.new("response.log")
37
+ @logger.level = Logger::DEBUG
38
+ end
39
+
40
+ # Process the SAML request, generate a response, and generate an auth form @.@
41
+ #**********************************************************************************
42
+ def process_response(samlRequest, relayState, username, form_only = nil)
43
+ xml_string = decodeAuthnRequestXML(samlRequest)
44
+ getRequestAttributes(xml_string)
45
+
46
+ samlResponse = createSamlResponse(username)
47
+
48
+ @logger.debug("\nSAML Response\n" + samlResponse.to_s()) if @logger
49
+
50
+ signedResponse = signXML(samlResponse)
51
+
52
+ @logger.debug("\nSigned Response\n" + signedResponse.to_s()) if @logger
53
+
54
+ @encodedresponse = Base64.encode64(signedResponse)
55
+
56
+ unless form_only
57
+ @authenticateform = "<html>
58
+ <head>
59
+ <script type=\"text/javascript\">
60
+ var t = setTimeout(\"document.acsForm.submit();\", 0);
61
+ </script>
62
+ </head>
63
+
64
+ <body>
65
+ <form name='acsForm' id='acsForm' action='#{@acs}' method='post'>
66
+ <input type='hidden' name='SAMLResponse' value=\"#{signedResponse}\" />
67
+ <input type='hidden' name='RelayState' value=\"#{relayState}\" />
68
+ </form>
69
+ <br/>
70
+ <br/>
71
+ <br/>
72
+ <center>
73
+ <h2>Redirecting...</h2>
74
+ </center>
75
+ </body>
76
+ </html>"
77
+ else
78
+ @authenticateform = "<form name='acsForm' id='acsForm' action='#{@acs}' method='post'>
79
+ <input type='hidden' name='SAMLResponse' value=\"#{signedResponse}\" />
80
+ <input type='hidden' name='RelayState' value=\"#{relayState}\" />
81
+ </form>"
82
+ end
83
+ end
84
+
85
+ # Generate a response, and generate an auth form @.@
86
+ #**********************************************************************************
87
+ def process_response_wo_request(username, form_only = nil)
88
+ samlResponse = createSamlResponse(username)
89
+
90
+ @logger.debug("\nSAML Response\n" + samlResponse.to_s()) if @logger
91
+
92
+ signedResponse = signXML(samlResponse)
93
+
94
+ @logger.debug("\nSigned Response\n" + signedResponse.to_s()) if @logger
95
+
96
+ @encodedresponse = Base64.encode64(signedResponse)
97
+
98
+ unless form_only
99
+ @authenticateform = "<html>
100
+ <head>
101
+ <script type=\"text/javascript\">
102
+ var t = setTimeout(\"document.acsForm.submit();\", 0);
103
+ </script>
104
+ </head>
105
+
106
+ <body>
107
+ <form name='acsForm' id='acsForm' action='#{@acs}' method='post'>
108
+ <input type='hidden' name='SAMLResponse' value=\"#{signedResponse}\" />
109
+ <input type='hidden' name='RelayState' value=\"#{@relayState}\" />
110
+ </form>
111
+ <br/>
112
+ <br/>
113
+ <br/>
114
+ <center>
115
+ <h2>Redirecting...</h2>
116
+ </center>
117
+ </body>
118
+ </html>"
119
+ else
120
+ @authenticateform = "<form name='acsForm' id='acsForm' action='#{@acs}' method='post'>
121
+ <input type='hidden' name='SAMLResponse' value=\"#{signedResponse}\" />
122
+ <input type='hidden' name='RelayState' value=\"#{@relayState}\" />
123
+ </form>"
124
+ end
125
+ end
126
+
127
+ # private
128
+
129
+ # Decode the SAML request @.@
130
+ #**********************************************************************************
131
+ def decodeAuthnRequestXML(encodedRequestXmlString)
132
+ unzipper = Zlib::Inflate.new( -Zlib::MAX_WBITS )
133
+ return unzipper.inflate(Base64.decode64( encodedRequestXmlString ))
134
+ end
135
+
136
+ # Get needed attributes from the decoded SAML request @.@
137
+ #**********************************************************************************
138
+ def getRequestAttributes(xmlString)
139
+ doc = Document.new( xmlString )
140
+ @issueinstant = doc.root.attributes["IssueInstant"]
141
+ @providername = doc.root.attributes["ProviderName"]
142
+ @acs = doc.root.attributes["AssertionConsumerServiceURL"]
143
+ @requestID = doc.root.attributes["ID"]
144
+ end
145
+
146
+ # Generate SAML response @.@
147
+ #**********************************************************************************
148
+ def createSamlResponse(authenticatedUser)
149
+ current_time = Time.new().utc().strftime("%Y-%m-%dT%H:%M:%SZ")
150
+ # 20 minutes after issued time
151
+ notOnOrAfter = (Time.new().utc()+60*20).strftime("%Y-%m-%dT%H:%M:%SZ")
152
+
153
+ samlResponse = ""
154
+ File.open(@response_template_path).each { |line|
155
+ samlResponse += line
156
+ }
157
+
158
+ samlResponse.gsub!("<USERNAME_STRING>", authenticatedUser)
159
+ samlResponse.gsub!("<RESPONSE_ID>", generateUniqueHexCode(42))
160
+ samlResponse.gsub!("<ISSUE_INSTANT>", current_time)
161
+ samlResponse.gsub!("<AUTHN_INSTANT>", current_time)
162
+ samlResponse.gsub!("<NOT_BEFORE>", @issueinstant)
163
+ samlResponse.gsub!("<NOT_ON_OR_AFTER>", notOnOrAfter)
164
+ samlResponse.gsub!("<ASSERTION_ID>", generateUniqueHexCode(42))
165
+ (@requestID == "") ? samlResponse.gsub!("InResponseTo=\"<REQUEST_ID>\"", @requestID) : samlResponse.gsub!("<REQUEST_ID>", @requestID)
166
+ samlResponse.gsub!("<RSADSA>", @crypt_type)
167
+ samlResponse.gsub!("<DESTINATION>", @acs)
168
+ samlResponse.gsub!("<ISSUER_DOMAIN>", @issuer)
169
+
170
+ return samlResponse
171
+ end
172
+
173
+ # Sign the SAML response @.@
174
+ #**********************************************************************************
175
+ def signXML(xml)
176
+ signature = ""
177
+ File.open(@signature_template_path).each { |line|
178
+ signature += line
179
+ }
180
+
181
+ document = Document.new(xml)
182
+ sigDoc = Document.new(signature)
183
+
184
+ # 3. Apply digesting algorithms over the resource, and calculate the digest value.
185
+ digestValue = calculateDigest(document)
186
+
187
+ # 4. Enclose the details in the <Reference> element.
188
+ # 5. Collect all <Reference> elements inside the <SignedInfo> element. Indicate the canonicalization and signature methodologies.
189
+ digestElement = XPath.first(sigDoc, "//DigestValue")
190
+ digestElement.add_text(digestValue)
191
+
192
+ # 6. Canonicalize contents of <SignedInfo>, apply the signature algorithm, and generate the XML Digital signature.
193
+ signedElement = XPath.first(sigDoc, "//SignedInfo")
194
+ signatureValue = calculateSignatureValue(signedElement)
195
+
196
+ # 7. Enclose the signature within the <SignatureValue> element.
197
+ signatureValueElement = XPath.first(sigDoc, "//SignatureValue")
198
+ signatureValueElement.add_text(signatureValue)
199
+
200
+ # 8. Add relevant key information, if any, and produce the <Signature> element.
201
+ cert = ""
202
+ File.open(@certificate_path).each { |line|
203
+ cert += line
204
+ }
205
+
206
+ cert.sub!(/.*BEGIN CERTIFICATE-----\s*(.*)\s*-----END CERT.*/m, '\1')
207
+
208
+ certNode = XPath.first(sigDoc, "//X509Certificate")
209
+ certNode.add_text(cert)
210
+
211
+ # 9. put it all together
212
+ status_child = document.elements["//samlp:Status"]
213
+ status_child.parent.insert_before(status_child, sigDoc.root)
214
+ retval = document.to_s()
215
+
216
+ return retval
217
+ end
218
+
219
+ # Generate Signature @.@
220
+ #**********************************************************************************
221
+ def calculateSignatureValue(element)
222
+ element.add_namespace("http://www.w3.org/2000/09/xmldsig#")
223
+ element.add_namespace("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol")
224
+ element.add_namespace("xmlns:xenc", "http://www.w3.org/2001/04/xmlenc#")
225
+
226
+ canoner = XmlCanonicalizer.new(false, true)
227
+ canon_element = canoner.canonicalize(element)
228
+
229
+ if @crypt_type == "rsa"
230
+ pkey = PKey::RSA.new(File::read(@private_key_path))
231
+ elsif @crypt_type == "dsa"
232
+ pkey = PKey::DSA.new(File::read(@private_key_path))
233
+ end
234
+
235
+ signature = Base64.encode64(pkey.sign(OpenSSL::Digest::SHA1.new, canon_element.to_s.chomp).chomp).chomp
236
+
237
+ element.delete_attribute("xmlns")
238
+ element.delete_attribute("xmlns:samlp")
239
+ element.delete_attribute("xmlns:xenc")
240
+ return signature
241
+ end
242
+
243
+ # Apply digesting algorithms over the resource, and calculate the digest value @.@
244
+ #**********************************************************************************
245
+ def calculateDigest(element)
246
+ canoner = XmlCanonicalizer.new(false, true)
247
+ canon_element = canoner.canonicalize(element)
248
+
249
+ element_hash = Base64.encode64(Digest::SHA1.digest(canon_element.to_s.chomp).chomp).chomp
250
+
251
+ return element_hash
252
+ end
253
+
254
+ # Create response ID @.@
255
+ #**********************************************************************************
256
+ def generateUniqueHexCode( codeLength )
257
+ validChars = ("A".."F").to_a + ("0".."9").to_a
258
+ length = validChars.size
259
+
260
+ validStartChars = ("A".."F").to_a
261
+ startLength = validStartChars.size
262
+
263
+ hexCode = ""
264
+ hexCode << validStartChars[rand(startLength-1)]
265
+
266
+ 1.upto(codeLength-1) { |i| hexCode << validChars[rand(length-1)] }
267
+
268
+ return hexCode
269
+ end
270
+ end
@@ -0,0 +1,395 @@
1
+ require "rexml/document"
2
+ require "base64"
3
+
4
+ include REXML
5
+
6
+
7
+ class REXML::Instruction
8
+ def write(writer, indent=-1, transitive=false, ie_hack=false)
9
+ indent(writer, indent)
10
+ writer << START.sub(/\\/u, '')
11
+ writer << @target
12
+ writer << ' '
13
+ writer << @content if @content != nil
14
+ writer << STOP.sub(/\\/u, '')
15
+ end
16
+ end
17
+
18
+ class REXML::Attribute
19
+ def <=>(a2)
20
+ if (self === a2)
21
+ return 0
22
+ elsif (self == nil)
23
+ return -1
24
+ elsif (a2 == nil)
25
+ return 1
26
+ elsif (self.prefix() == a2.prefix())
27
+ return self.name()<=>a2.name()
28
+ end
29
+ if (self.prefix() == nil)
30
+ return -1
31
+ elsif (a2.prefix() == nil)
32
+ return 1
33
+ end
34
+ ret = self.namespace()<=>a2.namespace()
35
+ if (ret == 0)
36
+ ret = self.prefix()<=>a2.prefix()
37
+ end
38
+ return ret
39
+ end
40
+ end
41
+
42
+ class REXML::Element
43
+ def search_namespace(prefix)
44
+ if (self.namespace(prefix) == nil)
45
+ return (self.parent().search_namespace(prefix)) if (self.parent() != nil)
46
+ else
47
+ return self.namespace(prefix)
48
+ end
49
+ end
50
+ def rendered=(rendered)
51
+ @rendered = rendered
52
+ end
53
+ def rendered?()
54
+ return @rendered
55
+ end
56
+ def node_namespaces()
57
+ ns = Array.new()
58
+ ns.push(self.prefix())
59
+ self.attributes().each_attribute{|a|
60
+ if (a.prefix() == "xmlns" or (a.prefix() == "" && a.local_name() == "xmlns"))
61
+ ns.push("xmlns")
62
+ end
63
+ }
64
+ self.prefixes().each { |prefix| ns.push(prefix) }
65
+ ns
66
+ end
67
+ end
68
+
69
+ class NamespaceNode
70
+ attr_reader :prefix, :uri
71
+ def initialize(prefix, uri)
72
+ @prefix = prefix
73
+ @uri = uri
74
+ end
75
+ end
76
+
77
+ class XmlCanonicalizer
78
+ attr_accessor :prefix_list, :logger
79
+
80
+ BEFORE_DOC_ELEMENT = 0
81
+ INSIDE_DOC_ELEMENT = 1
82
+ AFTER_DOC_ELEMENT = 2
83
+
84
+ NODE_TYPE_ATTRIBUTE = 3
85
+ NODE_TYPE_WHITESPACE = 4
86
+ NODE_TYPE_COMMENT = 5
87
+ NODE_TYPE_PI = 6
88
+ NODE_TYPE_TEXT = 7
89
+
90
+
91
+ def initialize(with_comments, excl_c14n)
92
+ @with_comments = with_comments
93
+ @exclusive = excl_c14n
94
+ @res = ""
95
+ @state = BEFORE_DOC_ELEMENT
96
+ @xnl = Array.new()
97
+ @prevVisibleNamespacesStart = 0
98
+ @prevVisibleNamespacesEnd = 0
99
+ @visibleNamespaces = Array.new()
100
+ @inclusive_namespaces = Array.new()
101
+ @prefix_list = nil
102
+ @rendered_prefixes = Array.new()
103
+ @logger = Logger.new("xmlcanonicalizer.log")
104
+ @logger.level = Logger::DEBUG
105
+ end
106
+
107
+ def add_inclusive_namespaces(prefix_list, element, visible_namespaces)
108
+ namespaces = element.attributes()
109
+ namespaces.each_attribute{|ns|
110
+ if (ns.prefix=="xmlns")
111
+ if (prefix_list.include?(ns.local_name()))
112
+ visible_namespaces.push(NamespaceNode.new("xmlns:"+ns.local_name(), ns.value()))
113
+ end
114
+ end
115
+ }
116
+ parent = element.parent()
117
+ add_inclusive_namespaces(prefix_list, parent, visible_namespaces) if (parent)
118
+ visible_namespaces
119
+ end
120
+
121
+ def canonicalize(document)
122
+ write_document_node(document)
123
+ @logger.debug("\nCanonicalized result\n" + @res.to_s()) if @logger
124
+ @res
125
+ end
126
+
127
+ def canonicalize_element(element, logging = true)
128
+ @logger.debug("Canonicalize element:\n" + element.to_s()) if @logger
129
+ @inclusive_namespaces = add_inclusive_namespaces(@prefix_list, element, @inclusive_namespaces) if (@prefix_list)
130
+ @preserve_document = element.document()
131
+ tmp_parent = element.parent()
132
+ body_string = remove_whitespace(element.to_s().gsub("\n","").gsub("\t","").gsub("\r",""))
133
+ document = Document.new(body_string)
134
+ tmp_parent.delete_element(element)
135
+ element = tmp_parent.add_element(document.root())
136
+ @preserve_element = element
137
+ document = Document.new(element.to_s())
138
+ ns = element.namespace(element.prefix())
139
+ document.root().add_namespace(element.prefix(), ns)
140
+ write_document_node(document)
141
+ @logger.debug("Canonicalized result:\n" + @res.to_s()) if @logger
142
+ @res
143
+ end
144
+
145
+ def write_document_node(document)
146
+ @state = BEFORE_DOC_ELEMENT
147
+ if (document.class().to_s() == "REXML::Element")
148
+ write_node(document)
149
+ else
150
+ document.each_child{|child|
151
+ write_node(child)
152
+ }
153
+ end
154
+ @res
155
+ end
156
+
157
+ def write_node(node)
158
+ visible = is_node_visible(node)
159
+ if ((node.node_type() == :text) && white_text?(node.value()))
160
+ res = node.value()
161
+ res.gsub("\r\n","\n")
162
+ #res = res.delete(" ").delete("\t")
163
+ res.delete("\r")
164
+ @res = @res + res
165
+ #write_text_node(node,visible) if (@state == INSIDE_DOC_ELEMENT)
166
+ return
167
+ end
168
+ if (node.node_type() == :text)
169
+ write_text_node(node, visible)
170
+ return
171
+ end
172
+ if (node.node_type() == :element)
173
+ write_element_node(node, visible) if (!node.rendered?())
174
+ node.rendered=(true)
175
+ end
176
+ if (node.node_type() == :processing_instruction)
177
+ end
178
+ if (node.node_type() == :comment)
179
+ end
180
+ end
181
+
182
+ def write_element_node(node, visible)
183
+ savedPrevVisibleNamespacesStart = @prevVisibleNamespacesStart
184
+ savedPrevVisibleNamespacesEnd = @prevVisibleNamespacesEnd
185
+ savedVisibleNamespacesSize = @visibleNamespaces.size()
186
+ state = @state
187
+ state = INSIDE_DOC_ELEMENT if (visible && state == BEFORE_DOC_ELEMENT)
188
+ @res = @res + "<" + node.expanded_name() if (visible)
189
+ write_namespace_axis(node, visible)
190
+ write_attribute_axis(node)
191
+ @res = @res + ">" if (visible)
192
+ node.each_child{|child|
193
+ write_node(child)
194
+ }
195
+ @res = @res + "</" +node.expanded_name() + ">" if (visible)
196
+ @state = AFTER_DOC_ELEMENT if (visible && state == BEFORE_DOC_ELEMENT)
197
+ @prevVisibleNamespacesStart = savedPrevVisibleNamespacesStart
198
+ @prevVisibleNamespacesEnd = savedPrevVisibleNamespacesEnd
199
+ @visibleNamespaces.slice!(savedVisibleNamespacesSize, @visibleNamespaces.size() - savedVisibleNamespacesSize) if (@visibleNamespaces.size() > savedVisibleNamespacesSize)
200
+ end
201
+
202
+ def write_namespace_axis(node, visible)
203
+ doc = node.document()
204
+ has_empty_namespace = false
205
+ list = Array.new()
206
+ cur = node
207
+ #while ((cur != nil) && (cur != doc) && (cur.node_type() != :document))
208
+ namespaces = cur.node_namespaces()
209
+ namespaces.each{|prefix|
210
+ next if ((prefix == "xmlns") && (node.namespace(prefix) == ""))
211
+ namespace = cur.namespace(prefix)
212
+ next if (is_namespace_node(namespace))
213
+ next if (node.namespace(prefix) != cur.namespace(prefix))
214
+ next if (prefix == "xml" && namespace == "http://www.w3.org/XML/1998/namespace")
215
+ next if (!is_node_visible(cur))
216
+ rendered = is_namespace_rendered(prefix, namespace)
217
+ @visibleNamespaces.push(NamespaceNode.new("xmlns:"+prefix,namespace)) if (visible)
218
+ if ((!rendered) && !list.include?(prefix))
219
+ list.push(prefix)
220
+ end
221
+ has_empty_namespace = true if (prefix == nil)
222
+ }
223
+ if (visible && !has_empty_namespace && !is_namespace_rendered(nil, nil))
224
+ @res = @res + ' xmlns=""'
225
+ end
226
+ #TODO: ns of inclusive_list
227
+ #=begin
228
+ if ((@prefix_list) && (node.to_s() == node.parent().to_s()))
229
+ #list.push(node.prefix())
230
+ @inclusive_namespaces.each{|ns|
231
+ prefix = ns.prefix().split(":")[1]
232
+ list.push(prefix) if (!list.include?(prefix) && (!node.attributes.prefixes.include?(prefix)))
233
+ }
234
+ @prefix_list = nil
235
+ end
236
+ #=end
237
+ list.sort!()
238
+ list.insert(0, "xmlns") unless list.delete("xmlns").nil?
239
+ list.each{|prefix|
240
+ next if (prefix == "")
241
+ next if (@rendered_prefixes.include?(prefix))
242
+ @rendered_prefixes.push(prefix)
243
+ ns = node.namespace(prefix)
244
+ ns = @preserve_element.namespace(prefix) if (ns == nil)
245
+ @res = @res + normalize_string(" " + prefix + '="' + ns + '"', NODE_TYPE_TEXT) if (prefix == "xmlns")
246
+ @res = @res + normalize_string(" xmlns:" + prefix + '="' + ns + '"', NODE_TYPE_TEXT) if (prefix != nil && prefix != "xmlns")
247
+ }
248
+ if (visible)
249
+ @prevVisibleNamespacesStart = @prevVisibleNamespacesEnd
250
+ @prevVisibleNamespacesEnd = @visibleNamespaces.size()
251
+ end
252
+ end
253
+
254
+ def write_attribute_axis(node)
255
+ list = Array.new()
256
+ node.attributes().sort.each{|key, attr|
257
+ list.push(attr) if (!is_namespace_node(attr.value()) && !is_namespace_decl(attr)) # && is_node_visible(
258
+ }
259
+ if (!@exclusive && node.parent() != nil && node.parent().parent() != nil)
260
+ cur = node.parent()
261
+ while (cur != nil)
262
+ #next if (cur.attributes() == nil)
263
+ cur.each_attribute{|attribute|
264
+ next if (attribute.prefix() != "xml")
265
+ next if (attribute.prefix().index("xmlns") == 0)
266
+ next if (node.namespace(attribute.prefix()) == attribute.value())
267
+ found = true
268
+ list.each{|n|
269
+ if (n.prefix() == "xml" && n.value() == attritbute.value())
270
+ found = true
271
+ break
272
+ end
273
+ }
274
+ next if (found)
275
+ list.push(attribute)
276
+ }
277
+ end
278
+ end
279
+ list.each{|attribute|
280
+ if (attribute != nil)
281
+ if (attribute.name() != "xmlns")
282
+ @res = @res + " " + normalize_string(attribute.to_string(), NODE_TYPE_ATTRIBUTE).gsub("'",'"')
283
+ end
284
+ # else
285
+ # @res = @res + " " + normalize_string(attribute.name()+'="'+attribute.to_s()+'"', NODE_TYPE_ATTRIBUTE).gsub("'",'"')
286
+ #end
287
+ end
288
+ }
289
+ end
290
+
291
+ def is_namespace_node(namespace_uri)
292
+ return (namespace_uri == "http://www.w3.org/2000/xmlns/")
293
+ end
294
+
295
+ def is_namespace_rendered(prefix, uri)
296
+ is_empty_ns = prefix == nil && uri == nil
297
+ if (is_empty_ns)
298
+ start = 0
299
+ else
300
+ start = @prevVisibleNamespacesStart
301
+ end
302
+ @visibleNamespaces.each{|ns|
303
+ if (ns.prefix() == "xmlns:"+prefix.to_s() && ns.uri() == uri)
304
+ return true
305
+ end
306
+ }
307
+ return is_empty_ns
308
+ #(@visibleNamespaces.size()-1).downto(start) {|i|
309
+ # ns = @visibleNamespaces[i]
310
+ # return true if (ns.prefix() == "xmlns:"+prefix.to_s() && ns.uri() == uri)
311
+ # #p = ns.prefix() if (ns.prefix().index("xmlns") == 0)
312
+ # #return ns.uri() == uri if (p == prefix)
313
+ #}
314
+ #return is_empty_ns
315
+ end
316
+
317
+ def is_node_visible(node)
318
+ return true if (@xnl.size() == 0)
319
+ @xnl.each{|element|
320
+ return true if (element == node)
321
+ }
322
+ return false
323
+ end
324
+
325
+ def normalize_string(input, type)
326
+ sb = ""
327
+ return input
328
+ end
329
+ #input.each_byte{|b|
330
+ # if (b ==60 && (type == NODE_TYPE_ATTRIBUTE || is_text_node(type)))
331
+ # sb = sb + "&lt;"
332
+ # elsif (b == 62 && is_text_node(type))
333
+ # sb = sb + "&gt;"
334
+ # elsif (b == 38 && (is_text_node(type) || is_text_node(type))) #Ampersand
335
+ # sb = sb + "&amp;"
336
+ # elsif (b == 34 && is_text_node(type)) #Quote
337
+ # sb = sb + "&quot;"
338
+ # elsif (b == 9 && is_text_node(type)) #Tabulator
339
+ # sb = sb + "&#x9;"
340
+ # elsif (b == 11 && is_text_node(type)) #CR
341
+ # sb = sb + "&#xA;"
342
+ # elsif (b == 13 && (type == NODE_TYPE_ATTRIBUTE || (is_text_node(type) && type != NODE_TYPE_WHITESPACE) || type == NODE_TYPE_COMMENT || type == NODE_TYPE_PI))
343
+ # sb = sb + "&#xD;"
344
+ # elsif (b == 13)
345
+ # next
346
+ # else
347
+ # sb = sb.concat(b)
348
+ # end
349
+ #}
350
+ #sb
351
+ #end
352
+
353
+ def write_text_node(node, visible)
354
+ if (visible)
355
+ @res = @res + normalize_string(node.value(), node.node_type())
356
+ end
357
+ end
358
+
359
+ def white_text?(text)
360
+ return true if ((text.strip() == "") || (text.strip() == nil))
361
+ return false
362
+ end
363
+
364
+ def is_namespace_decl(attribute)
365
+ #return true if (attribute.name() == "xmlns")
366
+ return true if (attribute.prefix().index("xmlns") == 0)
367
+ return false
368
+ end
369
+
370
+ def is_text_node(type)
371
+ return true if (type == NODE_TYPE_TEXT || type == NODE_TYPE_CDATA || type == NODE_TYPE_WHITESPACE)
372
+ return false
373
+ end
374
+
375
+ def remove_whitespace(string)
376
+ new_string = ""
377
+ in_white = false
378
+ string.each_byte{|b|
379
+ #if (in_white && b == 32)
380
+ #else
381
+ if !(in_white && b == 32)
382
+ new_string = new_string + b.chr()
383
+ end
384
+ if (b == 62) #>
385
+ in_white = true
386
+ end
387
+ if (b == 60) #<
388
+ in_white = false
389
+ end
390
+ }
391
+ new_string
392
+ end
393
+ end
394
+
395
+
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fractalpenguin-googlesso
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - FractalPenguin
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-09-19 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: The standard GoogleSSO gem with a few modifications and enhancments
23
+ email: kylerchrdsn@gmail.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - lib/FractalPenguinGoogleSSO.rb
32
+ - lib/processresponse.rb
33
+ - lib/xmlcanonicalizer.rb
34
+ - lib/SamlResponseTemplate.xml
35
+ - lib/SignatureTemplate.xml
36
+ has_rdoc: true
37
+ homepage: http://rubygems.org/gems/fractalpenguin-googlesso
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ hash: 3
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.4.2
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Google SSO
70
+ test_files: []
71
+