diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountCredentialResource.java b/services/src/main/java/org/keycloak/services/resources/account/AccountCredentialResource.java index 15f1935651..2653185346 100644 --- a/services/src/main/java/org/keycloak/services/resources/account/AccountCredentialResource.java +++ b/services/src/main/java/org/keycloak/services/resources/account/AccountCredentialResource.java @@ -10,8 +10,12 @@ import org.keycloak.credential.CredentialProvider; import org.keycloak.credential.CredentialTypeMetadata; import org.keycloak.credential.CredentialTypeMetadataContext; import org.keycloak.credential.UserCredentialStoreManager; -import org.keycloak.events.EventBuilder; -import org.keycloak.models.*; +import org.keycloak.models.AccountRoles; +import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.models.AuthenticationFlowModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.services.ErrorResponse; diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountLoader.java b/services/src/main/java/org/keycloak/services/resources/account/AccountLoader.java index de602d9cb2..67f755a2c7 100644 --- a/services/src/main/java/org/keycloak/services/resources/account/AccountLoader.java +++ b/services/src/main/java/org/keycloak/services/resources/account/AccountLoader.java @@ -18,6 +18,7 @@ package org.keycloak.services.resources.account; import org.jboss.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; +import org.jboss.resteasy.spi.HttpResponse; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.common.enums.AccountRestApiVersion; import org.keycloak.events.EventBuilder; @@ -28,6 +29,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.services.managers.AppAuthManager; import org.keycloak.services.managers.Auth; import org.keycloak.services.managers.AuthenticationManager; +import org.keycloak.services.resources.Cors; import org.keycloak.theme.Theme; import javax.ws.rs.HttpMethod; @@ -37,6 +39,7 @@ import javax.ws.rs.NotFoundException; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriInfo; @@ -51,6 +54,11 @@ public class AccountLoader { private KeycloakSession session; private EventBuilder event; + @Context + private HttpRequest request; + @Context + private HttpResponse response; + private static final Logger logger = Logger.getLogger(AccountLoader.class); public AccountLoader(KeycloakSession session, EventBuilder event) { @@ -94,6 +102,9 @@ public class AccountLoader { @Path("{version : v\\d[0-9a-zA-Z_\\-]*}") @Produces(MediaType.APPLICATION_JSON) public Object getVersionedAccountRestService(final @PathParam("version") String version) { + if (request.getHttpMethod().equals(HttpMethod.OPTIONS)) { + return new CorsPreflightService(request); + } return getAccountRestService(getAccountManagementClient(session.getContext().getRealm()), version); } @@ -121,6 +132,9 @@ public class AccountLoader { if (authResult == null) { throw new NotAuthorizedException("Bearer token required"); } + Auth auth = new Auth(session.getContext().getRealm(), authResult.getToken(), authResult.getUser(), client, authResult.getSession(), false); + + Cors.add(request).allowedOrigins(auth.getToken()).allowedMethods("GET", "PUT", "POST", "DELETE").auth().build(response); if (authResult.getUser().getServiceAccountClientLink() != null) { throw new NotAuthorizedException("Service accounts are not allowed to access this service"); @@ -137,7 +151,6 @@ public class AccountLoader { } } - Auth auth = new Auth(session.getContext().getRealm(), authResult.getToken(), authResult.getUser(), client, authResult.getSession(), false); AccountRestService accountRestService = new AccountRestService(session, auth, client, event, version); ResteasyProviderFactory.getInstance().injectProperties(accountRestService); accountRestService.init(); diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java b/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java index aa103720f1..c728f30873 100755 --- a/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java +++ b/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java @@ -19,8 +19,8 @@ package org.keycloak.services.resources.account; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.common.ClientConnection; -import org.keycloak.common.enums.AccountRestApiVersion; import org.keycloak.common.Profile; +import org.keycloak.common.enums.AccountRestApiVersion; import org.keycloak.common.util.StringPropertyReplacer; import org.keycloak.events.EventBuilder; import org.keycloak.events.EventStoreProvider; @@ -42,7 +42,6 @@ import org.keycloak.services.ErrorResponse; import org.keycloak.services.managers.Auth; import org.keycloak.services.managers.UserSessionManager; import org.keycloak.services.messages.Messages; -import org.keycloak.services.resources.Cors; import org.keycloak.services.resources.account.resources.ResourcesService; import org.keycloak.services.util.ResolveRelative; import org.keycloak.storage.ReadOnlyException; @@ -50,16 +49,15 @@ import org.keycloak.theme.Theme; import org.keycloak.userprofile.LegacyUserProfileProviderFactory; import org.keycloak.userprofile.UserProfile; import org.keycloak.userprofile.UserProfileProvider; -import org.keycloak.userprofile.utils.UserUpdateHelper; -import org.keycloak.userprofile.profile.representations.AccountUserRepresentationUserProfile; import org.keycloak.userprofile.profile.DefaultUserProfileContext; +import org.keycloak.userprofile.profile.representations.AccountUserRepresentationUserProfile; +import org.keycloak.userprofile.utils.UserUpdateHelper; import org.keycloak.userprofile.validation.UserProfileValidationResult; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.NotFoundException; -import javax.ws.rs.OPTIONS; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; @@ -71,7 +69,6 @@ import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.io.IOException; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -120,18 +117,6 @@ public class AccountRestService { eventStore = session.getProvider(EventStoreProvider.class); } - /** - * CORS preflight - * - * @return - */ - @Path("/") - @OPTIONS - @NoCache - public Response preflight() { - return Cors.add(request, Response.ok()).auth().preflight().build(); - } - /** * Get account information. * @@ -141,7 +126,7 @@ public class AccountRestService { @GET @Produces(MediaType.APPLICATION_JSON) @NoCache - public Response account() { + public UserRepresentation account() { auth.requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_PROFILE); UserModel user = auth.getUser(); @@ -161,7 +146,7 @@ public class AccountRestService { copiedAttributes.remove(UserModel.USERNAME); rep.setAttributes(copiedAttributes); - return Cors.add(request, Response.ok(rep)).auth().allowedOrigins(auth.getToken()).build(); + return rep; } @Path("/") @@ -189,7 +174,7 @@ public class AccountRestService { UserUpdateHelper.updateAccount(realm, user, updatedUser); event.success(); - return Cors.add(request, Response.noContent()).auth().allowedOrigins(auth.getToken()).build(); + return Response.noContent().build(); } catch (ReadOnlyException e) { return ErrorResponse.error(Messages.READ_ONLY_USER, Response.Status.BAD_REQUEST); } @@ -270,15 +255,15 @@ public class AccountRestService { ClientModel client = realm.getClientByClientId(clientId); if (client == null) { - return Cors.add(request, Response.status(Response.Status.NOT_FOUND).entity("No client with clientId: " + clientId + " found.")).build(); + return ErrorResponse.error("No client with clientId: " + clientId + " found.", Response.Status.NOT_FOUND); } UserConsentModel consent = session.users().getConsentByClient(realm, user.getId(), client.getId()); if (consent == null) { - return Cors.add(request, Response.noContent()).build(); + return Response.noContent().build(); } - return Cors.add(request, Response.ok(modelToRepresentation(consent))).build(); + return Response.ok(modelToRepresentation(consent)).build(); } /** @@ -299,14 +284,14 @@ public class AccountRestService { event.event(EventType.REVOKE_GRANT_ERROR); String msg = String.format("No client with clientId: %s found.", clientId); event.error(msg); - return Cors.add(request, Response.status(Response.Status.NOT_FOUND).entity(msg)).build(); + return ErrorResponse.error(msg, Response.Status.NOT_FOUND); } session.users().revokeConsentForClient(realm, user.getId(), client.getId()); new UserSessionManager(session).revokeOfflineToken(user, client); event.success(); - return Cors.add(request, Response.noContent()).build(); + return Response.noContent().build(); } /** @@ -359,7 +344,7 @@ public class AccountRestService { event.event(EventType.GRANT_CONSENT_ERROR); String msg = String.format("No client with clientId: %s found.", clientId); event.error(msg); - return Cors.add(request, Response.status(Response.Status.NOT_FOUND).entity(msg)).build(); + return ErrorResponse.error(msg, Response.Status.NOT_FOUND); } try { @@ -371,9 +356,9 @@ public class AccountRestService { } event.success(); grantedConsent = session.users().getConsentByClient(realm, user.getId(), client.getId()); - return Cors.add(request, Response.ok(modelToRepresentation(grantedConsent))).build(); + return Response.ok(modelToRepresentation(grantedConsent)).build(); } catch (IllegalArgumentException e) { - return Cors.add(request, Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage())).build(); + return ErrorResponse.error(e.getMessage(), Response.Status.BAD_REQUEST); } } @@ -416,7 +401,7 @@ public class AccountRestService { @GET @Produces(MediaType.APPLICATION_JSON) @NoCache - public Response applications(@QueryParam("name") String name) { + public List applications(@QueryParam("name") String name) { checkAccountApiEnabled(); auth.requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_APPLICATIONS); @@ -461,7 +446,7 @@ public class AccountRestService { } } - return Cors.add(request, Response.ok(apps)).auth().allowedOrigins(auth.getToken()).build(); + return apps; } private boolean matches(ClientModel client, String name) { diff --git a/services/src/main/java/org/keycloak/services/resources/account/SessionResource.java b/services/src/main/java/org/keycloak/services/resources/account/SessionResource.java index 56cee3fde9..c582d2fc15 100755 --- a/services/src/main/java/org/keycloak/services/resources/account/SessionResource.java +++ b/services/src/main/java/org/keycloak/services/resources/account/SessionResource.java @@ -24,6 +24,7 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -44,7 +45,6 @@ import org.keycloak.representations.account.DeviceRepresentation; import org.keycloak.representations.account.SessionRepresentation; import org.keycloak.services.managers.Auth; import org.keycloak.services.managers.AuthenticationManager; -import org.keycloak.services.resources.Cors; /** * @author Pedro Igor @@ -73,9 +73,8 @@ public class SessionResource { @GET @Produces(MediaType.APPLICATION_JSON) @NoCache - public Response toRepresentation() { - return Cors.add(request, Response.ok(session.sessions().getUserSessions(realm, user).stream() - .map(this::toRepresentation).collect(Collectors.toList()))).auth().allowedOrigins(auth.getToken()).build(); + public List toRepresentation() { + return session.sessions().getUserSessions(realm, user).stream().map(this::toRepresentation).collect(Collectors.toList()); } /** @@ -87,7 +86,7 @@ public class SessionResource { @GET @Produces(MediaType.APPLICATION_JSON) @NoCache - public Response devices() { + public Collection devices() { Map reps = new HashMap<>(); List sessions = session.sessions().getUserSessions(realm, user); @@ -117,7 +116,7 @@ public class SessionResource { rep.addSession(createSessionRepresentation(s, device)); } - return Cors.add(request, Response.ok(reps.values())).auth().allowedOrigins(auth.getToken()).build(); + return reps.values(); } /** @@ -139,7 +138,7 @@ public class SessionResource { } } - return Cors.add(request, Response.noContent()).auth().allowedOrigins(auth.getToken()).build(); + return Response.noContent().build(); } /** @@ -158,7 +157,7 @@ public class SessionResource { if (userSession != null && userSession.getUser().equals(user)) { AuthenticationManager.backchannelLogout(session, userSession, true); } - return Cors.add(request, Response.noContent()).auth().allowedOrigins(auth.getToken()).build(); + return Response.noContent().build(); } private SessionRepresentation createSessionRepresentation(UserSessionModel s, DeviceRepresentation device) { diff --git a/services/src/main/java/org/keycloak/services/resources/account/resources/AbstractResourceService.java b/services/src/main/java/org/keycloak/services/resources/account/resources/AbstractResourceService.java index 87f8b5fcf9..447bff7674 100644 --- a/services/src/main/java/org/keycloak/services/resources/account/resources/AbstractResourceService.java +++ b/services/src/main/java/org/keycloak/services/resources/account/resources/AbstractResourceService.java @@ -16,7 +16,6 @@ */ package org.keycloak.services.resources.account.resources; -import javax.ws.rs.core.Response; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -29,7 +28,6 @@ import java.util.stream.Collectors; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.model.PermissionTicket; -import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.store.PermissionTicketStore; import org.keycloak.authorization.store.ResourceStore; import org.keycloak.authorization.store.ScopeStore; @@ -42,7 +40,6 @@ import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.representations.idm.authorization.ScopeRepresentation; import org.keycloak.services.managers.Auth; -import org.keycloak.services.resources.Cors; /** * @author Pedro Igor @@ -69,10 +66,6 @@ public abstract class AbstractResourceService { uriInfo = session.getContext().getUri(); } - protected Response cors(Response.ResponseBuilder response) { - return Cors.add(request, response).auth().allowedOrigins(auth.getToken()).build(); - } - public static class Resource extends ResourceRepresentation { private Client client; diff --git a/services/src/main/java/org/keycloak/services/resources/account/resources/ResourceService.java b/services/src/main/java/org/keycloak/services/resources/account/resources/ResourceService.java index 7541137fb4..68ce9d6d49 100644 --- a/services/src/main/java/org/keycloak/services/resources/account/resources/ResourceService.java +++ b/services/src/main/java/org/keycloak/services/resources/account/resources/ResourceService.java @@ -66,8 +66,8 @@ public class ResourceService extends AbstractResourceService { */ @GET @Produces(MediaType.APPLICATION_JSON) - public Response getResource() { - return cors(Response.ok(new Resource(resource, provider))); + public Resource getResource() { + return new Resource(resource, provider); } /** @@ -78,7 +78,7 @@ public class ResourceService extends AbstractResourceService { @GET @Path("permissions") @Produces(MediaType.APPLICATION_JSON) - public Response toPermissions() { + public Collection toPermissions() { Map filters = new HashMap<>(); filters.put(PermissionTicket.OWNER, user.getId()); @@ -92,7 +92,7 @@ public class ResourceService extends AbstractResourceService { permissions = resources.iterator().next().getPermissions(); } - return cors(Response.ok(permissions)); + return permissions; } @GET @@ -101,9 +101,9 @@ public class ResourceService extends AbstractResourceService { public Response user(@QueryParam("value") String value) { try { final UserModel user = getUser(value); - return cors(Response.ok(toRepresentation(provider.getKeycloakSession(), provider.getRealm(), user))); + return Response.ok(toRepresentation(provider.getKeycloakSession(), provider.getRealm(), user)).build(); } catch (NotFoundException e) { - return cors(Response.noContent()); + return Response.noContent().build(); } } @@ -172,7 +172,7 @@ public class ResourceService extends AbstractResourceService { } } - return cors(Response.noContent()); + return Response.noContent().build(); } /** @@ -183,7 +183,7 @@ public class ResourceService extends AbstractResourceService { @GET @Path("permissions/requests") @Produces(MediaType.APPLICATION_JSON) - public Response getPermissionRequests() { + public Collection getPermissionRequests() { Map filters = new HashMap<>(); filters.put(PermissionTicket.OWNER, user.getId()); @@ -196,7 +196,7 @@ public class ResourceService extends AbstractResourceService { requests.computeIfAbsent(ticket.getRequester(), requester -> new Permission(ticket, provider)).addScope(ticket.getScope().getName()); } - return cors(Response.ok(requests.values())); + return requests.values(); } private void grantPermission(UserModel user, String scopeId) { diff --git a/services/src/main/java/org/keycloak/services/resources/account/resources/ResourcesService.java b/services/src/main/java/org/keycloak/services/resources/account/resources/ResourcesService.java index bccba19d82..0b040e58cf 100644 --- a/services/src/main/java/org/keycloak/services/resources/account/resources/ResourcesService.java +++ b/services/src/main/java/org/keycloak/services/resources/account/resources/ResourcesService.java @@ -203,10 +203,10 @@ public class ResourcesService extends AbstractResourceService { result = result.subList(0, size - 1); } - return cors(Response.ok().entity(result).links(createPageLinks(first, max, size))); + return Response.ok().entity(result).links(createPageLinks(first, max, size)).build(); } - return cors(Response.ok().entity(query.apply(-1, -1).collect(Collectors.toList()))); + return Response.ok().entity(query.apply(-1, -1).collect(Collectors.toList())).build(); } private Link[] createPageLinks(Integer first, Integer max, int resultSize) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceCorsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceCorsTest.java index 941a644b94..c6c0ba7312 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceCorsTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceCorsTest.java @@ -22,6 +22,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.keycloak.common.enums.AccountRestApiVersion; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.testsuite.AbstractTestRealmKeycloakTest; import org.keycloak.testsuite.AssertEvents; @@ -34,6 +35,10 @@ import java.io.IOException; import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude; import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + /** * @author Stian Thorgersen */ @@ -67,6 +72,7 @@ public class AccountRestServiceCorsTest extends AbstractTestRealmKeycloakTest { @Override public void configureTestRealm(RealmRepresentation testRealm) { + testRealm.setEditUsernameAllowed(false); } @Rule @@ -76,69 +82,84 @@ public class AccountRestServiceCorsTest extends AbstractTestRealmKeycloakTest { public void testGetProfile() throws IOException, InterruptedException { driver.navigate().to(VALID_CORS_URL); - doJsGet(executor, getAccountUrl(), tokenUtil.getToken(), true); + doXhr(executor, getAccountUrl(), tokenUtil.getToken(), null, true); } @Test public void testGetProfileInvalidOrigin() throws IOException, InterruptedException { driver.navigate().to(INVALID_CORS_URL); - doJsGet(executor, getAccountUrl(), tokenUtil.getToken(), false); + doXhr(executor, getAccountUrl(), tokenUtil.getToken(), null, false); } @Test public void testUpdateProfile() throws IOException { driver.navigate().to(VALID_CORS_URL); - doJsPost(executor, getAccountUrl(), tokenUtil.getToken(), "{ \"firstName\" : \"Bob\" }", true); + doXhr(executor, getAccountUrl(), tokenUtil.getToken(), "{ \"firstName\" : \"Bob\" }", true); } @Test public void testUpdateProfileInvalidOrigin() throws IOException { driver.navigate().to(INVALID_CORS_URL); - doJsPost(executor, getAccountUrl(), tokenUtil.getToken(), "{ \"firstName\" : \"Bob\" }", false); + doXhr(executor, getAccountUrl(), tokenUtil.getToken(), "{ \"firstName\" : \"Bob\" }", false); + } + + @Test + public void testErrorResponse() { + driver.navigate().to(VALID_CORS_URL); + + Result result = doXhr(executor, getAccountUrl(), tokenUtil.getToken(), "{ \"username\" : \"vmuzikar\" }", true); + assertEquals(400, result.getStatus()); + assertThat(result.getResult(), containsString("readOnlyUsernameMessage")); + } + + @Test + public void testErrorResponseInvalidOrigin() { + driver.navigate().to(INVALID_CORS_URL); + + doXhr(executor, getAccountUrl(), tokenUtil.getToken(), "{ \"username\" : \"vmuzikar\" }", false); + } + + @Test + public void testGetVersionedApi() { + driver.navigate().to(VALID_CORS_URL); + + doXhr(executor, getAccountUrl() + "/" + AccountRestApiVersion.DEFAULT.getStrVersion(), tokenUtil.getToken(), null, true); + } + + @Test + public void testGetVersionedApiInvalidOrigin() { + driver.navigate().to(INVALID_CORS_URL); + + doXhr(executor, getAccountUrl() + "/" + AccountRestApiVersion.DEFAULT.getStrVersion(), tokenUtil.getToken(), null, false); } private String getAccountUrl() { return suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth/realms/test/account"; } - private Result doJsGet(JavascriptExecutor executor, String url, String token, boolean expectAllowed) { + private Result doXhr(JavascriptExecutor executor, String url, String token, String postData, boolean expectAllowed) { String js = "var r = new XMLHttpRequest();" + - "var r = new XMLHttpRequest();" + - "r.open('GET', '" + url + "', false);" + - "r.setRequestHeader('Accept','application/json');" + - "r.setRequestHeader('Authorization','bearer " + token + "');" + - "r.send();" + - "return r.status + ':::' + r.responseText"; - return doXhr(executor, js, expectAllowed); - } - - private Result doJsPost(JavascriptExecutor executor, String url, String token, String data, boolean expectAllowed) { - String js = "var r = new XMLHttpRequest();" + - "var r = new XMLHttpRequest();" + - "r.open('POST', '" + url + "', false);" + + "r.open('" + (postData == null ? "GET" : "POST") + "', '" + url + "', false);" + "r.setRequestHeader('Accept','application/json');" + "r.setRequestHeader('Content-Type','application/json');" + "r.setRequestHeader('Authorization','bearer " + token + "');" + - "r.send('" + data + "');" + + "r.send(" + (postData == null ? "" : "'" + postData + "'") + ");" + "return r.status + ':::' + r.responseText"; - return doXhr(executor, js, expectAllowed); - } - private Result doXhr(JavascriptExecutor executor, String js, boolean expectAllowed) { Result result = null; Throwable error = null; try { String response = (String) executor.executeScript(js); - String r[] = response.split(":::"); + String[] r = response.split(":::"); result = new Result(Integer.parseInt(r[0]), r.length == 2 ? r[1] : null); } catch (Throwable t ) { error = t; } - if (result == null || (result.getStatus() != 200 && result.getStatus() != 204) || error != null) { + if (error != null) { if (expectAllowed) { throw new AssertionError("Cors request failed: " + WebDriverLogDumper.dumpBrowserLogs(driver)); } else {