Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public Response ldapServerCapabilities(TestLdapConnectionRepresentation config)
Set<LDAPCapabilityRepresentation> ldapCapabilities = LDAPServerCapabilitiesManager.queryServerCapabilities(config, session, realm);
return Response.ok().entity(ldapCapabilities).build();
} catch (Exception e) {
return ErrorResponse.error("ldapServerCapabilities error", Response.Status.BAD_REQUEST);
throw ErrorResponse.error("ldapServerCapabilities error", Response.Status.BAD_REQUEST);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ public Response testLDAPConnection(@FormParam("action") String action, @FormPara

TestLdapConnectionRepresentation config = new TestLdapConnectionRepresentation(action, connectionUrl, bindDn, bindCredential, useTruststoreSpi, connectionTimeout, startTls, LDAPConstants.AUTH_TYPE_SIMPLE);
config.setComponentId(componentId);
boolean result = LDAPServerCapabilitiesManager.testLDAP(config, session, realm);
return result ? Response.noContent().build() : ErrorResponse.error("LDAP test error", Response.Status.BAD_REQUEST);
if (! LDAPServerCapabilitiesManager.testLDAP(config, session, realm)) {
throw ErrorResponse.error("LDAP test error", Response.Status.BAD_REQUEST);
}
return Response.noContent().build();
}

/**
Expand All @@ -84,8 +86,10 @@ public Response testLDAPConnection(@FormParam("action") String action, @FormPara
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response testLDAPConnection(TestLdapConnectionRepresentation config) {
boolean result = LDAPServerCapabilitiesManager.testLDAP(config, session, realm);
return result ? Response.noContent().build() : ErrorResponse.error("LDAP test error", Response.Status.BAD_REQUEST);
if (! LDAPServerCapabilitiesManager.testLDAP(config, session, realm)) {
throw ErrorResponse.error("LDAP test error", Response.Status.BAD_REQUEST);
}
return Response.noContent().build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@
import org.keycloak.common.util.Time;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;

import org.keycloak.models.utils.KeycloakModelUtils;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
Expand All @@ -42,52 +44,53 @@ public class EventBuilder {

private static final Logger log = Logger.getLogger(EventBuilder.class);

private final KeycloakSessionFactory sessionFactory;
private EventStoreProvider store;
private List<EventListenerProvider> listeners;
private RealmModel realm;
private Event event;
private Boolean storeImmediately;

public EventBuilder(RealmModel realm, KeycloakSession session, ClientConnection clientConnection) {
this(realm, session);
ipAddress(clientConnection.getRemoteAddr());
}

public EventBuilder(RealmModel realm, KeycloakSession session) {
this.sessionFactory = session.getKeycloakSessionFactory();
this.realm = realm;

event = new Event();

if (realm.isEventsEnabled()) {
EventStoreProvider store = session.getProvider(EventStoreProvider.class);
if (store != null) {
this.store = store;
} else {
log.error("Events enabled, but no event store provider configured");
}
this.store = realm.isEventsEnabled() ? session.getProvider(EventStoreProvider.class) : null;
if (realm.isEventsEnabled() && this.store == null) {
log.error("Events enabled, but no event store provider configured");
}


this.listeners = realm.getEventsListenersStream()
.map(id -> {
EventListenerProvider listener = session.getProvider(EventListenerProvider.class, id);
if (listener != null) {
return listener;
} else {
log.error("Event listener '" + id + "' registered, but provider not found");
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
this.listeners = getEventListeners(session, realm);

realm(realm);
}

private EventBuilder(EventStoreProvider store, List<EventListenerProvider> listeners, RealmModel realm, Event event) {
this.store = store;
private static List<EventListenerProvider> getEventListeners(KeycloakSession session, RealmModel realm) {
return realm.getEventsListenersStream().map(id -> {
EventListenerProvider listener = session.getProvider(EventListenerProvider.class, id);
if (listener != null) {
return listener;
} else {
log.error("Event listener '" + id + "' registered, but provider not found");
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}

private EventBuilder(KeycloakSessionFactory sessionFactory, List<EventListenerProvider> listeners, RealmModel realm, Event event) {
this.listeners = listeners;
this.realm = realm;
this.event = event;
this.sessionFactory = sessionFactory;
}

public EventBuilder realm(RealmModel realm) {
Expand Down Expand Up @@ -179,7 +182,20 @@ public EventBuilder detail(String key, Stream<String> values) {
}
return detail(key, values.filter(Objects::nonNull).collect(Collectors.joining("::")));
}


/**
* Sets the time when to store the event.
* By default, events marked as success ({@link #success()}) are stored upon commit of the session's transaction
* while the failures ({@link #error(java.lang.String)} are stored and propagated to the event listeners
* immediately into the event store.
* @param forcedValue If {@code true}, the event is stored in the event store immediately. If {@code false},
* the event is stored upon commit.
* @return
*/
public EventBuilder storeImmediately(boolean forcedValue) {
this.storeImmediately = forcedValue;
return this;
}

public EventBuilder removeDetail(String key) {
if (event.getDetails() != null) {
Expand All @@ -193,7 +209,7 @@ public Event getEvent() {
}

public void success() {
send();
send(this.storeImmediately == null ? false : this.storeImmediately);
}

public void error(String error) {
Expand All @@ -205,31 +221,42 @@ public void error(String error) {
event.setType(EventType.valueOf(event.getType().name() + "_ERROR"));
}
event.setError(error);
send();
send(this.storeImmediately == null ? true : this.storeImmediately);
}

public EventBuilder clone() {
return new EventBuilder(store, listeners, realm, event.clone());
return new EventBuilder(sessionFactory, listeners, realm, event.clone());
}

private void send() {
private void send(boolean sendImmediately) {
event.setTime(Time.currentTimeMillis());
event.setId(UUID.randomUUID().toString());

if (store != null) {
Set<String> eventTypes = realm.getEnabledEventTypesStream().collect(Collectors.toSet());
if (!eventTypes.isEmpty() ? eventTypes.contains(event.getType().name()) : event.getType().isSaveByDefault()) {
store.onEvent(event);
Set<String> eventTypes = realm.getEnabledEventTypesStream().collect(Collectors.toSet());
if (sendImmediately) {
KeycloakModelUtils.runJobInTransaction(sessionFactory, session -> {
EventStoreProvider store = session.getProvider(EventStoreProvider.class);
List<EventListenerProvider> listeners = getEventListeners(session, realm);

sendNow(store, eventTypes, listeners);
});
} else {
sendNow(this.store, eventTypes, this.listeners);
}
}

private void sendNow(EventStoreProvider targetStore, Set<String> eventTypes, List<EventListenerProvider> targetListeners) {
if (targetStore != null) {
if (eventTypes.isEmpty() && event.getType().isSaveByDefault() || eventTypes.contains(event.getType().name())) {
targetStore.onEvent(event);
}
}

if (listeners != null) {
for (EventListenerProvider l : listeners) {
try {
l.onEvent(event);
} catch (Throwable t) {
log.error("Failed to send type to " + l, t);
}
for (EventListenerProvider l : targetListeners) {
try {
l.onEvent(event);
} catch (Throwable t) {
log.error("Failed to send type to " + l, t);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public Response delete(@PathParam("id") String id) {

List<Resource> resources = storeFactory.getResourceStore().findByScopes(resourceServer, Collections.singleton(scope));
if (!resources.isEmpty()) {
return ErrorResponse.error("Scopes can not be removed while associated with resources.", Status.BAD_REQUEST);
throw ErrorResponse.error("Scopes can not be removed while associated with resources.", Status.BAD_REQUEST);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ public void prepare(PartialImportRepresentation partialImportRep,
}

protected ErrorResponseException existsError(String message) {
Response error = ErrorResponse.exists(message);
return new ErrorResponseException(error);
throw ErrorResponse.exists(message);
}

protected PartialImportResult overwritten(String modelId, T resourceRep){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,12 @@ public void prepare(PartialImportRepresentation partialImportRep, RealmModel rea
}

protected ErrorResponseException exists(String message) {
Response error = ErrorResponse.exists(message);
return new ErrorResponseException(error);
throw ErrorResponse.exists(message);
}

protected ErrorResponseException noClientFound(String clientId) {
String message = "Can not import client roles for nonexistent client named " + clientId;
Response error = ErrorResponse.error(message, Response.Status.PRECONDITION_FAILED);
return new ErrorResponseException(error);
throw ErrorResponse.error(message, Response.Status.PRECONDITION_FAILED);
}

public PartialImportResult overwritten(String clientId, String modelId, RoleRepresentation roleRep) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public PartialImportResults doImport(PartialImportRepresentation rep, RealmModel
RepresentationToModel.importRoles(rep.getRoles(), realm);
} catch (Exception e) {
ServicesLogger.LOGGER.roleImportError(e);
throw new ErrorResponseException(ErrorResponse.error(e.getMessage(), Response.Status.INTERNAL_SERVER_ERROR));
throw ErrorResponse.error(e.getMessage(), Response.Status.INTERNAL_SERVER_ERROR);
}

// add "add" results for new roles created
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,28 +28,28 @@
*/
public class ErrorResponse {

public static Response exists(String message) {
public static ErrorResponseException exists(String message) {
return ErrorResponse.error(message, Response.Status.CONFLICT);
}

public static Response error(String message, Response.Status status) {
public static ErrorResponseException error(String message, Response.Status status) {
return ErrorResponse.error(message, null, status);
}

public static Response error(String message, Object[] params, Response.Status status) {
public static ErrorResponseException error(String message, Object[] params, Response.Status status) {
ErrorRepresentation error = new ErrorRepresentation();
error.setErrorMessage(message);
error.setParams(params);
return Response.status(status).entity(error).type(MediaType.APPLICATION_JSON).build();
return new ErrorResponseException(Response.status(status).entity(error).type(MediaType.APPLICATION_JSON).build());
}

public static Response errors(List<ErrorRepresentation> s, Response.Status status) {
public static ErrorResponseException errors(List<ErrorRepresentation> s, Response.Status status) {
return errors(s, status, true);
}

public static Response errors(List<ErrorRepresentation> s, Response.Status status, boolean shrinkSingleError) {
public static ErrorResponseException errors(List<ErrorRepresentation> s, Response.Status status, boolean shrinkSingleError) {
if (shrinkSingleError && s.size() == 1) {
return Response.status(status).entity(s.get(0)).type(MediaType.APPLICATION_JSON).build();
return new ErrorResponseException(Response.status(status).entity(s.get(0)).type(MediaType.APPLICATION_JSON).build());
}
ErrorRepresentation error = new ErrorRepresentation();
error.setErrors(s);
Expand All @@ -58,6 +58,6 @@ public static Response errors(List<ErrorRepresentation> s, Response.Status statu
error.setParams(s.get(0).getParams());
error.setField(s.get(0).getField());
}
return Response.status(status).entity(error).type(MediaType.APPLICATION_JSON).build();
return new ErrorResponseException(Response.status(status).entity(error).type(MediaType.APPLICATION_JSON).build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

package org.keycloak.services;

import org.keycloak.common.util.Resteasy;
import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;

import javax.ws.rs.WebApplicationException;
Expand Down Expand Up @@ -53,6 +55,16 @@ public String getErrorDescription() {

@Override
public Response getResponse() {
KeycloakSession session = Resteasy.getContextData(KeycloakSession.class);
if (session != null) {
// This has to happen, since calling getResponse() with non-null result leads to
// directly returning the result instead of
// propagating exception to KeycloakErrorHandler.toResponse(Throwable) which would ensure rollback on other exception types.
//
// See org.jboss.resteasy.core.ExceptionHandler.unwrapException(HttpRequest, Throwable, RESTEasyTracingLogger)

session.getTransactionManager().setRollbackOnly();
}
if (response != null) {
return response;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1175,7 +1175,7 @@ private Response redirectToErrorPage(AuthenticationSessionModel authSession, Res
return webEx.getResponse();
}

return ErrorPage.error(this.session, authSession, status, message, parameters);
throw new ErrorPageException(this.session, authSession, status, message, parameters);
}

private Response redirectToAccountErrorPage(AuthenticationSessionModel authSession, String message, Object ... parameters) {
Expand Down Expand Up @@ -1222,17 +1222,17 @@ protected Response browserAuthentication(AuthenticationSessionModel authSession,

private Response badRequest(String message) {
fireErrorEvent(message);
return ErrorResponse.error(message, Response.Status.BAD_REQUEST);
throw ErrorResponse.error(message, Response.Status.BAD_REQUEST);
}

private Response forbidden(String message) {
fireErrorEvent(message);
return ErrorResponse.error(message, Response.Status.FORBIDDEN);
throw ErrorResponse.error(message, Response.Status.FORBIDDEN);
}

private Response notFound(String message) {
fireErrorEvent(message);
return ErrorResponse.error(message, Response.Status.NOT_FOUND);
throw ErrorResponse.error(message, Response.Status.NOT_FOUND);
}

public static IdentityProvider getIdentityProvider(KeycloakSession session, RealmModel realm, String alias) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ public void setLabel(final @PathParam("credentialId") String credentialId, Strin
String label = JsonSerialization.readValue(userLabel, String.class);
user.credentialManager().updateCredentialLabel(credentialId, label);
} catch (IOException ioe) {
throw new ErrorResponseException(ErrorResponse.error(Messages.INVALID_REQUEST, Response.Status.BAD_REQUEST));
throw ErrorResponse.error(Messages.INVALID_REQUEST, Response.Status.BAD_REQUEST);
}
}

Expand Down
Loading