Skip to content

Commit 9358576

Browse files
committed
Implemented SAML authentication and retrieved rh_sso cookie
1 parent c9afcf0 commit 9358576

1 file changed

Lines changed: 170 additions & 50 deletions

File tree

windows/developer_platform_installer.iss

Lines changed: 170 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,6 @@ Root: HKLM; Subkey: "Software\Red Hat\{#AppName}"; Flags: uninsdeletekey
4747
Root: HKLM; Subkey: "Software\Red Hat\{#AppName}"; ValueType: string; ValueName: "InstallDir"; ValueData: "{app}"
4848

4949
[Code]
50-
//function xxxx
51-
//external 'xxx@files:EfTidy.dll stdcall setuponly';
52-
5350
5451
type
5552
ComponentGroup = record
@@ -64,6 +61,13 @@ type
6461
Install: Boolean;
6562
end;
6663
64+
SAMLFormParams = record
65+
Action: String;
66+
SAMLRequest: String;
67+
SAMLResponse: String;
68+
RelayState: String;
69+
end;
70+
6771
var
6872
// Page IDs
6973
AuthPageID, ComponentPageID, DownloadPageID, GetStartedPageID, InstallPageID : Integer;
@@ -104,13 +108,107 @@ begin
104108
end;
105109
end;
106110
107-
procedure LoginButtonOnClick(Sender: TObject);
111+
// Converts a value to hex
112+
function DigitToHex(Digit: Integer): Char;
113+
begin
114+
if (Digit >= 0) and (Digit <= 9) then
115+
Result := Chr(Digit + Ord('0'))
116+
else if (Digit >= 10) and (Digit <= 15) then
117+
Result := Chr(Digit - 10 + Ord('A'))
118+
else
119+
Result := '0';
120+
end;
121+
122+
// URL-encodes the provided string
123+
function URLEncode(const S: string): string;
124+
var
125+
i, idx, len: Integer;
126+
begin
127+
len := 0;
128+
for i := 1 to Length(S) do
129+
if ((S[i] >= '0') and (S[i] <= '9')) or
130+
((S[i] >= 'A') and (S[i] <= 'Z')) or
131+
((S[i] >= 'a') and (S[i] <= 'z')) or (S[i] = ' ') or
132+
(S[i] = '_') or (S[i] = '*') or (S[i] = '-') or (S[i] = '.') then
133+
len := len + 1
134+
else
135+
len := len + 3;
136+
137+
SetLength(Result, len);
138+
idx := 1;
139+
140+
for i := 1 to Length(S) do
141+
if S[i] = ' ' then
142+
begin
143+
Result[idx] := '+';
144+
idx := idx + 1;
145+
end
146+
else if ((S[i] >= '0') and (S[i] <= '9')) or
147+
((S[i] >= 'A') and (S[i] <= 'Z')) or
148+
((S[i] >= 'a') and (S[i] <= 'z')) or
149+
(S[i] = '_') or (S[i] = '*') or (S[i] = '-') or (S[i] = '.') then
150+
begin
151+
Result[idx] := S[i];
152+
idx := idx + 1;
153+
end
154+
else
155+
begin
156+
Result[idx] := '%';
157+
Result[idx + 1] := DigitToHex(Ord(S[i]) div 16);
158+
Result[idx + 2] := DigitToHex(Ord(S[i]) mod 16);
159+
idx := idx + 3;
160+
end;
161+
end;
162+
163+
function ExtractSAMLFormValues(ResponseText: String): SAMLFormParams;
108164
var
109-
Url, Resource, ResponseText, SAMLRequest, RelayState: String;
110165
I, J, K: Integer;
166+
XMLDoc, NodeList, FormNode, InputNode: Variant;
167+
begin
168+
XMLDoc := CreateOleObject('MSXML2.DOMDocument');
169+
XMLDoc.async := False;
170+
XMLDoc.resolveExternals := False;
171+
XMLDoc.validateOnParse := False;
172+
XMLDoc.setProperty('ProhibitDTD', False);
173+
XMLDoc.loadXML(ResponseText);
174+
175+
if XMLDoc.parseError.errorCode <> 0 then
176+
begin
177+
MsgBox('Error on line ' + IntToStr(XMLDoc.parseError.line) + ', position ' +
178+
IntToStr(XMLDoc.parseError.linepos) + ': ' + XMLDoc.parseError.reason, mbInformation, MB_OK);
179+
end else begin
180+
NodeList := XMLDoc.getElementsByTagName('form');
181+
182+
for I := 0 to NodeList.length - 1 do
183+
begin
184+
FormNode := NodeList.item(i);
185+
186+
Result.Action := FormNode.attributes.getNamedItem('action').nodeValue;
187+
188+
for J := 0 to FormNode.childNodes.length - 1 do
189+
begin
190+
if FormNode.childNodes.item(J).nodeName = 'input' then
191+
begin
192+
InputNode := FormNode.childNodes.item[J];
193+
194+
if InputNode.attributes.getNamedItem('name').nodeValue = 'SAMLRequest' then
195+
Result.SAMLRequest := InputNode.attributes.getNamedItem('value').nodeValue;
196+
if InputNode.attributes.getNamedItem('name').nodeValue = 'SAMLResponse' then
197+
Result.SAMLResponse := InputNode.attributes.getNamedItem('value').nodeValue;
198+
if InputNode.attributes.getNamedItem('name').nodeValue = 'RelayState' then
199+
Result.RelayState := InputNode.attributes.getNamedItem('value').nodeValue;
200+
end;
201+
end;
202+
end;
203+
end;
204+
end;
205+
206+
procedure LoginButtonOnClick(Sender: TObject);
207+
var
208+
Url, Resource, ResponseText, RequestText: String;
111209
Page: TWizardPage;
112-
Button: TNewButton;
113-
WinHttpReq, EfTidy, XMLDoc, NodeList, FormNode, InputNode: Variant;
210+
WinHttpReq, EfTidy: Variant;
211+
SAMLValues: SAMLFormParams;
114212
begin
115213
Page := PageFromID(AuthPageID);
116214
@@ -127,9 +225,13 @@ begin
127225
128226
// Perform a SAML authentication for redhat.com
129227
WinHttpReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
130-
WinHttpReq.Open('POST', Url, false);
228+
WinHttpReq.Open('GET', Url, false);
131229
WinHttpReq.SetClientCertificate('LOCAL_MACHINE\Personal\My Certificate');
132230
WinHttpReq.SetRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
231+
232+
// Temporary - set proxy so Fiddler can inspect the traffic
233+
//WinHttpReq.SetProxy( 2, '127.0.0.1:8888');
234+
133235
WinHttpReq.Send();
134236
135237
if WinHttpReq.Status <> 200 then
@@ -144,64 +246,82 @@ begin
144246
AuthLabel.Caption := 'Authentication Successful.';
145247
AuthLabel.Font.Color := clGreen;
146248
147-
//MsgBox(WinHttpReq.ResponseText, mbInformation, MB_OK);
148-
149-
150-
// Perform a SAML authentication for redhat.com
151-
WinHttpReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
152-
WinHttpReq.Open('GET', Resource, false);
153-
WinHttpReq.SetClientCertificate('LOCAL_MACHINE\Personal\My Certificate');
154-
WinHttpReq.SetRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
155-
WinHttpReq.Send();
156-
157-
ResponseText := WinHttpReq.ResponseText;
158-
249+
// Register the EfTidy dll so that we can call its functions
159250
ExtractTemporaryFile('EfTidy.dll');
160251
RegisterServer(True, ExpandConstant('{tmp}\EfTidy.dll'), False);
161252
162-
// Tidy up the (non well-formed) response with EfTidy
253+
// We are going to tidy up the (non well-formed) response with EfTidy -
254+
// the first step is to create the EfTidy object
163255
EfTidy := CreateOleObject('EfTidy.tidyCom');
164256
EfTidy.Option.Clean := True;
165257
EfTidy.Option.OutputType := 1; // XhtmlOut
166258
EfTidy.Option.DoctypeMode := 3; // DoctypeLoose
167-
ResponseText := EfTidy.TidyMemToMem(ResponseText);
168259
169-
XMLDoc := CreateOleObject('MSXML2.DOMDocument');
170-
XMLDoc.async := False;
171-
XMLDoc.resolveExternals := False;
172-
XMLDoc.validateOnParse := False;
173-
XMLDoc.setProperty('ProhibitDTD', False);
174-
XMLDoc.loadXML(ResponseText);
260+
// Tidy up the response to make it valid XML so we can parse it
261+
ResponseText := EfTidy.TidyMemToMem(WinHttpReq.ResponseText);
262+
263+
//MsgBox('Got response: ' + ResponseText, mbInformation, MB_OK);
175264
176-
if XMLDoc.parseError.errorCode <> 0 then
265+
// Parse the now-valid XML response and extract the values we're interested in
266+
SAMLValues := ExtractSAMLFormValues(ResponseText);
267+
268+
RequestText := 'SAMLRequest=' + URLEncode(SAMLValues.SAMLRequest) +
269+
'&RelayState=' + URLEncode(SAMLValues.RelayState);
270+
271+
// POST the SAMLRequest and RelayState to the URL specified in the returned action attribute,
272+
// which in this case should be the IdP
273+
WinHttpReq.Open('POST', SAMLValues.Action, false);
274+
WinHttpReq.SetRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
275+
WinHttpReq.SetRequestHeader('Content-Length', IntToStr(Length(RequestText)));
276+
WinHttpReq.Send(RequestText);
277+
278+
//MsgBox('Sending request: ' + RequestText, mbInformation, MB_OK);
279+
280+
if WinHttpReq.Status <> 200 then
177281
begin
178-
MsgBox('Error on line ' + IntToStr(XMLDoc.parseError.line) + ', position ' +
179-
IntToStr(XMLDoc.parseError.linepos) + ': ' + XMLDoc.parseError.reason, mbInformation, MB_OK);
282+
AuthLabel.Caption := 'Authentication Failed.';
283+
AuthLabel.Font.Color := clRed;
284+
Exit;
180285
end else begin
181-
NodeList := XMLDoc.getElementsByTagName('form');
286+
// Tidy up the response to make it valid XML so we can parse it
287+
ResponseText := EfTidy.TidyMemToMem(WinHttpReq.ResponseText);
182288
183-
for I := 0 to NodeList.length - 1 do
184-
begin
185-
FormNode := NodeList.item(i);
289+
//MsgBox('Got response: ' + ResponseText, mbInformation, MB_OK);
186290
187-
for J := 0 to FormNode.childNodes.length - 1 do
188-
begin
189-
if FormNode.childNodes.item(J).nodeName = 'input' then
190-
begin
191-
InputNode := FormNode.childNodes.item[J];
192-
193-
if InputNode.attributes.getNamedItem('name').nodeValue = 'SAMLRequest' then
194-
SAMLRequest := InputNode.attributes.getNamedItem('value').nodeValue;
195-
if InputNode.attributes.getNamedItem('name').nodeValue = 'RelayState' then
196-
RelayState := InputNode.attributes.getNamedItem('value').nodeValue;
197-
end;
198-
end;
199-
end;
200-
end;
291+
// Extract the Action, SAMLResponse and RelayState parameter values from the response
292+
SAMLValues := ExtractSAMLFormValues(ResponseText);
293+
294+
{MsgBox('Posting SAMLResponse to ' + SAMLValues.Action + ', Request length: ' +
295+
IntToStr(Length(SAMLValues.SAMLResponse)) +
296+
', SAMLResponse: ' + Copy(SAMLValues.SAMLResponse, 1, 1000) +
297+
', RelayState: ' + SAMLValues.RelayState,
298+
mbInformation, MB_OK);}
299+
300+
RequestText := 'SAMLResponse=' + URLEncode(SAMLValues.SAMLResponse) +
301+
'&RelayState=' + URLEncode(SAMLValues.RelayState);
302+
303+
//MsgBox('Sending request: ' + Copy(RequestText, 0, 1000), mbInformation, MB_OK);
304+
WinHttpReq.Open('POST', SAMLValues.Action, false);
201305
306+
// Do not follow redirects here
307+
WinHttpReq.Option(6) := False;
202308
203-
//MsgBox('SAMLRequest: ' + SAMLRequest + ' RelayState: ' + RelayState, mbInformation, MB_OK);
309+
WinHttpReq.SetRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
310+
WinHttpReq.SetRequestHeader('Content-Length', IntToStr(Length(RequestText)));
311+
WinHttpReq.Send(RequestText);
204312
313+
if WinHttpReq.Status <> 302 then
314+
begin
315+
AuthLabel.Caption := 'Authentication Failed.';
316+
AuthLabel.Font.Color := clRed;
317+
Exit;
318+
end else begin
319+
MsgBox('Got cookies: ' + WinHttpReq.getResponseHeader('Set-Cookie'), mbInformation, MB_OK);
320+
321+
//MsgBox('Got response code: ' + WinHttpReq.Status + ', response: ' +
322+
// WinHttpReq.ResponseText, mbInformation, MB_OK);
323+
end;
324+
end;
205325
206326
// Simulate a click of the Next button
207327
WizardForm.NextButton.OnClick(nil);

0 commit comments

Comments
 (0)