Repository navigation
[#1170] WindowsDesktopSSO: complete /json/authenticate from a Negotiate header in one request #1177
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f36a006
34e3959
e6cc8ad
81cbe13
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,9 @@ | |
|
|
||
| package org.forgerock.openam.core.rest.authn; | ||
|
|
||
| import com.sun.identity.authentication.callbacks.NameValueOutputCallback; | ||
| import com.sun.identity.authentication.callbacks.ScriptTextOutputCallback; | ||
| import com.sun.identity.authentication.spi.HttpCallback; | ||
| import org.forgerock.json.JsonValue; | ||
| import org.forgerock.openam.core.rest.authn.callbackhandlers.RestAuthCallbackHandler; | ||
| import org.forgerock.openam.core.rest.authn.exceptions.RestAuthResponseException; | ||
|
|
@@ -26,6 +29,8 @@ | |
| import org.testng.annotations.Test; | ||
|
|
||
| import javax.security.auth.callback.Callback; | ||
| import javax.security.auth.callback.NameCallback; | ||
| import javax.security.auth.callback.PasswordCallback; | ||
| import jakarta.servlet.http.HttpServletRequest; | ||
| import jakarta.servlet.http.HttpServletResponse; | ||
| import jakarta.ws.rs.core.HttpHeaders; | ||
|
|
@@ -36,6 +41,7 @@ | |
| import static org.mockito.Mockito.never; | ||
| import static org.mockito.Mockito.verify; | ||
| import static org.testng.Assert.assertEquals; | ||
| import static org.testng.Assert.assertNull; | ||
| import static org.testng.Assert.fail; | ||
|
|
||
| public class RestAuthCallbackHandlerManagerTest { | ||
|
|
@@ -291,4 +297,119 @@ public void shouldFailToHandleJsonCallbacksWithMissingJSONCallback() throws Rest | |
| //Then | ||
| fail(); | ||
| } | ||
|
|
||
| @Test | ||
| public void shouldSubmitCallbacksWhenHttpCallbackHeaderIsPresentAndScriptCannotBeFilled() | ||
| throws RestAuthException { | ||
|
|
||
| //Given | ||
| RestAuthCallbackHandlerManager manager = | ||
| new RestAuthCallbackHandlerManager(RestAuthCallbackHandlerFactory.getInstance()); | ||
| HttpServletRequest request = postRequest(); | ||
| HttpServletResponse response = mock(HttpServletResponse.class); | ||
| HttpCallback httpCallback = new HttpCallback("Authorization", "WWW-Authenticate:Negotiate", "401"); | ||
| ScriptTextOutputCallback script = new ScriptTextOutputCallback("p.set('skipKerberos', 'true');"); | ||
| given(request.getHeader("Authorization")).willReturn("Negotiate YIIB"); | ||
|
|
||
| //When | ||
| JsonValue jsonCallbacks = manager.handleCallbacks(request, response, new Callback[]{httpCallback, script}); | ||
|
|
||
| //Then | ||
| assertEquals(jsonCallbacks.size(), 0); | ||
| assertEquals(httpCallback.getAuthorization(), "Negotiate YIIB"); | ||
| } | ||
|
|
||
| @Test | ||
| public void shouldSubmitNameValueOutputCallbackWhenHttpCallbackHeaderIsPresent() throws RestAuthException { | ||
|
|
||
| //Given | ||
| RestAuthCallbackHandlerManager manager = | ||
| new RestAuthCallbackHandlerManager(RestAuthCallbackHandlerFactory.getInstance()); | ||
| HttpServletRequest request = postRequest(); | ||
| HttpServletResponse response = mock(HttpServletResponse.class); | ||
| HttpCallback httpCallback = new HttpCallback("Authorization", "WWW-Authenticate:Negotiate", "401"); | ||
| NameValueOutputCallback nameValue = new NameValueOutputCallback("realm", "/"); | ||
| given(request.getHeader("Authorization")).willReturn("Negotiate YIIB"); | ||
|
|
||
| //When | ||
| JsonValue jsonCallbacks = manager.handleCallbacks(request, response, | ||
| new Callback[]{httpCallback, nameValue}); | ||
|
|
||
| //Then | ||
| assertEquals(jsonCallbacks.size(), 0); | ||
| } | ||
|
|
||
| @Test | ||
| public void shouldChallengeWhenHttpCallbackFollowsOutputCallback() throws RestAuthException { | ||
|
|
||
| //Given | ||
| RestAuthCallbackHandlerManager manager = | ||
| new RestAuthCallbackHandlerManager(RestAuthCallbackHandlerFactory.getInstance()); | ||
| HttpServletRequest request = postRequest(); | ||
| HttpServletResponse response = mock(HttpServletResponse.class); | ||
| HttpCallback httpCallback = new HttpCallback("Authorization", "WWW-Authenticate:Negotiate", "401"); | ||
| ScriptTextOutputCallback script = new ScriptTextOutputCallback("p.set('skipKerberos', 'true');"); | ||
|
|
||
| //When | ||
| try { | ||
| manager.handleCallbacks(request, response, new Callback[]{script, httpCallback}); | ||
| fail("Expected a 401 challenge for the missing Authorization header"); | ||
| } catch (RestAuthResponseException e) { | ||
| //Then | ||
| assertEquals(e.getStatusCode(), 401); | ||
| assertEquals(e.getResponseHeaders().get("WWW-Authenticate"), "Negotiate"); | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| public void shouldFillOtherCallbacksFromRequestWhenHttpCallbackHeaderIsPresent() throws RestAuthException { | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No case covers an input callback that cannot be filled next to an
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added I also added |
||
| //Given | ||
| RestAuthCallbackHandlerManager manager = | ||
| new RestAuthCallbackHandlerManager(RestAuthCallbackHandlerFactory.getInstance()); | ||
| HttpServletRequest request = postRequest(); | ||
| HttpServletResponse response = mock(HttpServletResponse.class); | ||
| HttpCallback httpCallback = new HttpCallback("Authorization", "WWW-Authenticate:Negotiate", "401"); | ||
| NameCallback nameCallback = new NameCallback("User Name:"); | ||
| ScriptTextOutputCallback script = new ScriptTextOutputCallback("p.set('skipKerberos', 'true');"); | ||
| given(request.getHeader("Authorization")).willReturn("Negotiate YIIB"); | ||
| given(request.getHeader("X-OpenAM-Username")).willReturn("demo"); | ||
|
|
||
| //When | ||
| JsonValue jsonCallbacks = manager.handleCallbacks(request, response, | ||
| new Callback[]{httpCallback, nameCallback, script}); | ||
|
|
||
| //Then | ||
| assertEquals(jsonCallbacks.size(), 0); | ||
| assertEquals(nameCallback.getName(), "demo"); | ||
| } | ||
|
|
||
| @Test | ||
| public void shouldRejectHttpCallbackNextToInputCallbackThatCannotBeFilled() throws RestAuthException { | ||
|
|
||
| //Given | ||
| RestAuthCallbackHandlerManager manager = | ||
| new RestAuthCallbackHandlerManager(RestAuthCallbackHandlerFactory.getInstance()); | ||
| HttpServletRequest request = postRequest(); | ||
| HttpServletResponse response = mock(HttpServletResponse.class); | ||
| HttpCallback httpCallback = new HttpCallback("Authorization", "WWW-Authenticate:Negotiate", "401"); | ||
| PasswordCallback passwordCallback = new PasswordCallback("Password:", false); | ||
| given(request.getHeader("Authorization")).willReturn("Negotiate YIIB"); | ||
|
|
||
| //When | ||
| try { | ||
| manager.handleCallbacks(request, response, new Callback[]{httpCallback, passwordCallback}); | ||
| fail("Expected a 400 for the PasswordCallback that cannot be filled from the request"); | ||
| } catch (RestAuthException e) { | ||
| //Then | ||
| assertEquals(e.getStatusCode(), 400); | ||
| assertNull(passwordCallback.getPassword()); | ||
| } | ||
| } | ||
|
|
||
| private HttpServletRequest postRequest() { | ||
| HttpServletRequest request = mock(HttpServletRequest.class); | ||
| given(request.getMethod()).willReturn("POST"); | ||
| return request; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
await resp.text()is evaluated on success as well, after the XUI may already have navigated to the success URL. Chromium can drop the previous document's response bodies on navigation, and thenresp.text()throwsNetwork.getResponseBody: No resource with given identifier found. The test fails even though Kerberos SSO worked. The old 400 assertion never navigated, so it could not hit this. Reading the body only on failure avoids it:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 81cbe13: the body is read only when the status is not 200.