Merge pull request #1100 from stianst/master

Admin audit events
This commit is contained in:
Stian Thorgersen 2015-03-30 15:45:25 +02:00
commit 99b67dfd6c
64 changed files with 865 additions and 333 deletions

View file

@ -25,6 +25,7 @@ import org.keycloak.broker.provider.FederatedIdentity;
import org.keycloak.broker.provider.IdentityBrokerException; import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.events.Errors; import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder; import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventGroup;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.JWSInput;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
@ -32,7 +33,6 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken; import org.keycloak.representations.IDToken;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.EventsManager;
import org.keycloak.services.messages.Messages; import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.IdentityBrokerService; import org.keycloak.services.resources.IdentityBrokerService;
import org.keycloak.services.resources.RealmsResource; import org.keycloak.services.resources.RealmsResource;
@ -88,14 +88,14 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
UserSessionModel userSession = session.sessions().getUserSession(realm, state); UserSessionModel userSession = session.sessions().getUserSession(realm, state);
if (userSession == null) { if (userSession == null) {
logger.error("no valid user session"); logger.error("no valid user session");
EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder(); EventBuilder event = new EventBuilder(EventGroup.USER, realm, session, clientConnection);
event.event(EventType.LOGOUT); event.event(EventType.LOGOUT);
event.error(Errors.USER_SESSION_NOT_FOUND); event.error(Errors.USER_SESSION_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR); return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
} }
if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) { if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) {
logger.error("usersession in different state"); logger.error("usersession in different state");
EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder(); EventBuilder event = new EventBuilder(EventGroup.USER, realm, session, clientConnection);
event.event(EventType.LOGOUT); event.event(EventType.LOGOUT);
event.error(Errors.USER_SESSION_NOT_FOUND); event.error(Errors.USER_SESSION_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.SESSION_NOT_ACTIVE); return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.SESSION_NOT_ACTIVE);

View file

@ -9,6 +9,7 @@ import org.keycloak.broker.provider.IdentityProvider;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.Errors; import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder; import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventGroup;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
@ -18,7 +19,6 @@ import org.keycloak.protocol.saml.SAMLRequestParser;
import org.keycloak.protocol.saml.SamlProtocol; import org.keycloak.protocol.saml.SamlProtocol;
import org.keycloak.protocol.saml.SamlProtocolUtils; import org.keycloak.protocol.saml.SamlProtocolUtils;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.EventsManager;
import org.keycloak.services.messages.Messages; import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.flows.Flows; import org.keycloak.services.resources.flows.Flows;
import org.picketlink.common.constants.GeneralConstants; import org.picketlink.common.constants.GeneralConstants;
@ -163,7 +163,7 @@ public class SAMLEndpoint {
} }
public Response execute(String samlRequest, String samlResponse, String relayState) { public Response execute(String samlRequest, String samlResponse, String relayState) {
event = new EventsManager(realm, session, clientConnection).createEventBuilder(); event = new EventBuilder(EventGroup.USER, realm, session, clientConnection);
Response response = basicChecks(samlRequest, samlResponse); Response response = basicChecks(samlRequest, samlResponse);
if (response != null) return response; if (response != null) return response;
if (samlRequest != null) return handleSamlRequest(samlRequest, relayState); if (samlRequest != null) return handleSamlRequest(samlRequest, relayState);

View file

@ -161,5 +161,18 @@
<dropTable tableName="REALM_SOCIAL_CONFIG" cascadeConstraints="true" /> <dropTable tableName="REALM_SOCIAL_CONFIG" cascadeConstraints="true" />
<dropColumn tableName="CLIENT" columnName="ALLOWED_CLAIMS_MASK" /> <dropColumn tableName="CLIENT" columnName="ALLOWED_CLAIMS_MASK" />
<createTable tableName="REALM_ENABLED_EVENT_TYPES">
<column name="REALM_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="VALUE" type="VARCHAR(255)"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_ENABLED_EVENT_TYPES" constraintName="FK_H846O4H0W8EPX5NWEDRF5Y69J" referencedColumnNames="ID" referencedTableName="REALM"/>
<addColumn tableName="EVENT_ENTITY">
<column name="EVENT_GROUP" type="VARCHAR(255)"/>
<column name="REPRESENTATION" type="BLOB"/>
</addColumn>
</changeSet> </changeSet>
</databaseChangeLog> </databaseChangeLog>

View file

@ -10,6 +10,7 @@ public class RealmEventsConfigRepresentation {
protected boolean eventsEnabled; protected boolean eventsEnabled;
protected Long eventsExpiration; protected Long eventsExpiration;
protected List<String> eventsListeners; protected List<String> eventsListeners;
protected List<String> enabledEventTypes;
public boolean isEventsEnabled() { public boolean isEventsEnabled() {
return eventsEnabled; return eventsEnabled;
@ -34,4 +35,12 @@ public class RealmEventsConfigRepresentation {
public void setEventsListeners(List<String> eventsListeners) { public void setEventsListeners(List<String> eventsListeners) {
this.eventsListeners = eventsListeners; this.eventsListeners = eventsListeners;
} }
public List<String> getEnabledEventTypes() {
return enabledEventTypes;
}
public void setEnabledEventTypes(List<String> enabledEventTypes) {
this.enabledEventTypes = enabledEventTypes;
}
} }

View file

@ -68,6 +68,7 @@ public class RealmRepresentation {
protected Boolean eventsEnabled; protected Boolean eventsEnabled;
protected Long eventsExpiration; protected Long eventsExpiration;
protected List<String> eventsListeners; protected List<String> eventsListeners;
protected List<String> enabledEventTypes;
private List<IdentityProviderRepresentation> identityProviders; private List<IdentityProviderRepresentation> identityProviders;
private List<ProtocolMapperRepresentation> protocolMappers; private List<ProtocolMapperRepresentation> protocolMappers;
private Boolean identityFederationEnabled; private Boolean identityFederationEnabled;
@ -504,6 +505,14 @@ public class RealmRepresentation {
this.eventsListeners = eventsListeners; this.eventsListeners = eventsListeners;
} }
public List<String> getEnabledEventTypes() {
return enabledEventTypes;
}
public void setEnabledEventTypes(List<String> enabledEventTypes) {
this.enabledEventTypes = enabledEventTypes;
}
public List<UserFederationProviderRepresentation> getUserFederationProviders() { public List<UserFederationProviderRepresentation> getUserFederationProviders() {
return userFederationProviders; return userFederationProviders;
} }

View file

@ -13,7 +13,7 @@ public interface Details {
String RESPONSE_TYPE = "response_type"; String RESPONSE_TYPE = "response_type";
String AUTH_METHOD = "auth_method"; String AUTH_METHOD = "auth_method";
String IDENTITY_PROVIDER = "identity_provider"; String IDENTITY_PROVIDER = "identity_provider";
String IDENTITY_PROVIDER_IDENTITY = "identity_provider_identity"; String IDENTITY_PROVIDER_USERNAME = "identity_provider_identity";
String REGISTER_METHOD = "register_method"; String REGISTER_METHOD = "register_method";
String USERNAME = "username"; String USERNAME = "username";
String REMEMBER_ME = "remember_me"; String REMEMBER_ME = "remember_me";
@ -23,4 +23,10 @@ public interface Details {
String UPDATED_REFRESH_TOKEN_ID = "updated_refresh_token_id"; String UPDATED_REFRESH_TOKEN_ID = "updated_refresh_token_id";
String NODE_HOST = "node_host"; String NODE_HOST = "node_host";
String REASON = "reason"; String REASON = "reason";
String REALM = "realm";
String REPRESENTATION = "representation";
String APPLICATION_CLUSTER_NODE = "application_cluster_node";
} }

View file

@ -12,6 +12,8 @@ public class Event {
private EventType type; private EventType type;
private EventGroup group;
private String realmId; private String realmId;
private String clientId; private String clientId;
@ -24,6 +26,8 @@ public class Event {
private String error; private String error;
private String representation;
private Map<String, String> details; private Map<String, String> details;
public long getTime() { public long getTime() {
@ -42,6 +46,14 @@ public class Event {
this.type = type; this.type = type;
} }
public EventGroup getGroup() {
return group;
}
public void setGroup(EventGroup group) {
this.group = group;
}
public String getRealmId() { public String getRealmId() {
return realmId; return realmId;
} }
@ -90,6 +102,14 @@ public class Event {
this.error = error; this.error = error;
} }
public String getRepresentation() {
return representation;
}
public void setRepresentation(String representation) {
this.representation = representation;
}
public Map<String, String> getDetails() { public Map<String, String> getDetails() {
return details; return details;
} }
@ -102,13 +122,15 @@ public class Event {
Event clone = new Event(); Event clone = new Event();
clone.time = time; clone.time = time;
clone.type = type; clone.type = type;
clone.group = group;
clone.realmId = realmId; clone.realmId = realmId;
clone.clientId = clientId; clone.clientId = clientId;
clone.userId = userId; clone.userId = userId;
clone.sessionId = sessionId; clone.sessionId = sessionId;
clone.ipAddress = ipAddress; clone.ipAddress = ipAddress;
clone.error = error; clone.error = error;
clone.details = details != null ? new HashMap<String, String>(details) : null; clone.details = details != null ? new HashMap<>(details) : null;
clone.representation = representation;
return clone; return clone;
} }

View file

@ -1,12 +1,17 @@
package org.keycloak.events; package org.keycloak.events;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.ClientConnection;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
/** /**
@ -16,18 +21,47 @@ public class EventBuilder {
private static final Logger log = Logger.getLogger(EventBuilder.class); private static final Logger log = Logger.getLogger(EventBuilder.class);
private EventStoreProvider store;
private List<EventListenerProvider> listeners; private List<EventListenerProvider> listeners;
private RealmModel realm;
private Event event; private Event event;
public EventBuilder(List<EventListenerProvider> listeners, RealmModel realm, String ipAddress) { public EventBuilder(EventGroup group, RealmModel realm, KeycloakSession session, ClientConnection clientConnection) {
this.listeners = listeners; this.realm = realm;
this.event = new Event();
event = new Event();
event.setGroup(group);
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");
}
}
if (realm.getEventsListeners() != null && !realm.getEventsListeners().isEmpty()) {
this.listeners = new LinkedList<>();
for (String id : realm.getEventsListeners()) {
EventListenerProvider listener = session.getProvider(EventListenerProvider.class, id);
if (listener != null) {
listeners.add(listener);
} else {
log.error("Event listener '" + id + "' registered, but provider not found");
}
}
}
realm(realm); realm(realm);
ipAddress(ipAddress); ipAddress(clientConnection.getRemoteAddr());
} }
EventBuilder() { private EventBuilder(EventStoreProvider store, List<EventListenerProvider> listeners, RealmModel realm, Event event) {
this.store = store;
this.listeners = listeners;
this.realm = realm;
this.event = event;
} }
public EventBuilder realm(RealmModel realm) { public EventBuilder realm(RealmModel realm) {
@ -92,6 +126,18 @@ public class EventBuilder {
return this; return this;
} }
public EventBuilder representation(Object value) {
if (value == null || value.equals("")) {
return this;
}
try {
event.setRepresentation(JsonSerialization.writeValueAsPrettyString(value));
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
public EventBuilder removeDetail(String key) { public EventBuilder removeDetail(String key) {
if (event.getDetails() != null) { if (event.getDetails() != null) {
event.getDetails().remove(key); event.getDetails().remove(key);
@ -114,27 +160,22 @@ public class EventBuilder {
} }
public EventBuilder clone() { public EventBuilder clone() {
EventBuilder clone = new EventBuilder(); return new EventBuilder(store, listeners, realm, event.clone());
clone.listeners = listeners;
clone.event = event.clone();
return clone;
}
public EventBuilder reset() {
Event old = event;
event = new Event();
event.setRealmId(old.getRealmId());
event.setIpAddress(old.getIpAddress());
event.setClientId(old.getClientId());
event.setUserId(old.getUserId());
return this;
} }
private void send() { private void send() {
event.setTime(System.currentTimeMillis()); event.setTime(System.currentTimeMillis());
if (store != null) {
if (realm.getEnabledEventTypes() != null && !realm.getEnabledEventTypes().isEmpty() ? realm.getEnabledEventTypes().contains(event.getType().name()) : event.getType().isSaveByDefault()) {
try {
store.onEvent(event);
} catch (Throwable t) {
log.error("Failed to save event", t);
}
}
}
if (listeners != null) { if (listeners != null) {
for (EventListenerProvider l : listeners) { for (EventListenerProvider l : listeners) {
try { try {

View file

@ -0,0 +1,11 @@
package org.keycloak.events;
/**
* @author <a href="mailto:giriraj.sharma27@gmail.com">Giriraj Sharma</a>
*/
public enum EventGroup {
ADMIN,
USER
}

View file

@ -5,57 +5,125 @@ package org.keycloak.events;
*/ */
public enum EventType { public enum EventType {
LOGIN, LOGIN(true),
LOGIN_ERROR, LOGIN_ERROR(true),
REGISTER, REGISTER(true),
REGISTER_ERROR, REGISTER_ERROR(true),
LOGOUT, LOGOUT(true),
LOGOUT_ERROR, LOGOUT_ERROR(true),
CODE_TO_TOKEN,
CODE_TO_TOKEN_ERROR,
REFRESH_TOKEN,
VALIDATE_ACCESS_TOKEN,
VALIDATE_ACCESS_TOKEN_ERROR,
REFRESH_TOKEN_ERROR,
SOCIAL_LINK,
SOCIAL_LINK_ERROR,
REMOVE_FEDERATED_IDENTITY,
REMOVE_SOCIAL_LINK_ERROR,
UPDATE_EMAIL, CODE_TO_TOKEN(true),
UPDATE_EMAIL_ERROR, CODE_TO_TOKEN_ERROR(true),
UPDATE_PROFILE,
UPDATE_PROFILE_ERROR,
UPDATE_PASSWORD,
UPDATE_PASSWORD_ERROR,
UPDATE_TOTP,
UPDATE_TOTP_ERROR,
VERIFY_EMAIL,
VERIFY_EMAIL_ERROR,
REMOVE_TOTP, REFRESH_TOKEN(false),
REMOVE_TOTP_ERROR, REFRESH_TOKEN_ERROR(false),
VALIDATE_ACCESS_TOKEN(false),
VALIDATE_ACCESS_TOKEN_ERROR(false),
SEND_VERIFY_EMAIL, FEDERATED_IDENTITY_LINK(true),
SEND_VERIFY_EMAIL_ERROR, FEDERATED_IDENTITY_LINK_ERROR(true),
SEND_RESET_PASSWORD, REMOVE_FEDERATED_IDENTITY(true),
SEND_RESET_PASSWORD_ERROR, REMOVE_FEDERATED_IDENTITY_ERROR(true),
RESET_PASSWORD,
RESET_PASSWORD_ERROR,
INVALID_SIGNATURE_ERROR, UPDATE_EMAIL(true),
REGISTER_NODE, UPDATE_EMAIL_ERROR(true),
UNREGISTER_NODE, UPDATE_PROFILE(true),
UPDATE_PROFILE_ERROR(true),
UPDATE_PASSWORD(true),
UPDATE_PASSWORD_ERROR(true),
UPDATE_TOTP(true),
UPDATE_TOTP_ERROR(true),
VERIFY_EMAIL(true),
VERIFY_EMAIL_ERROR(true),
USER_INFO_REQUEST, REMOVE_TOTP(true),
USER_INFO_REQUEST_ERROR, REMOVE_TOTP_ERROR(true),
SEND_VERIFY_EMAIL(true),
SEND_VERIFY_EMAIL_ERROR(true),
SEND_RESET_PASSWORD(true),
SEND_RESET_PASSWORD_ERROR(true),
RESET_PASSWORD(true),
RESET_PASSWORD_ERROR(true),
INVALID_SIGNATURE_ERROR(false),
REGISTER_NODE(false),
UNREGISTER_NODE(false),
USER_INFO_REQUEST(false),
USER_INFO_REQUEST_ERROR(false),
IDENTITY_PROVIDER_LOGIN(false),
IDENTITY_PROVIDER_LOGIN_ERROR(false),
IDENTITY_PROVIDER_RESPONSE(false),
IDENTITY_PROVIDER_RESPONSE_ERROR(false),
IDENTITY_PROVIDER_RETRIEVE_TOKEN(false),
IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR(false),
IDENTITY_PROVIDER_ACCCOUNT_LINKING(false),
IDENTITY_PROVIDER_ACCCOUNT_LINKING_ERROR(false),
VIEW_REALM(false),
CREATE_REALM(false),
UPDATE_REALM(false),
DELETE_REALM(false),
VIEW_APPLICATIONS(false),
VIEW_APPLICATION(false),
CREATE_APPLICATION(false),
UPDATE_APPLICATION(false),
DELETE_APPLICATION(false),
VIEW_APPLICATION_USER_SESSIONS(false),
LOGOUT_APPLICATION_USERS(false),
LOGOUT_USER(false),
REGISTER_APPLICATION_CLUSTER_NODE(false),
UNREGISTER_APPLICATION_CLUSTER_NODE(false),
VIEW_CLIENT_CERTIFICATE(false),
UPDATE_CLIENT_CERTIFICATE(false),
VIEW_IDENTITY_PROVIDERS(false),
VIEW_IDENTITY_PROVIDER(false),
CREATE_IDENTITY_PROVIDER(false),
UPDATE_IDENTITY_PROVIDER(false),
DELETE_IDENTITY_PROVIDER(false),
VIEW_OAUTH_CLIENTS(false),
VIEW_OAUTH_CLIENT(false),
CREATE_OAUTH_CLIENT(false),
UPDATE_OAUTH_CLIENT(false),
DELETE_OAUTH_CLIENT(false),
VIEW_ROLES(false),
VIEW_ROLE(false),
CREATE_ROLE(false),
UPDATE_ROLE(false),
DELETE_ROLE(false),
VIEW_USERS(false),
VIEW_USER(false),
CREATE_USER(false),
UPDATE_USER(false),
DELETE_USER(false),
VIEW_USER_SESSIONS(false),
LOGOUT_USER_SESSIONS(false),
VIEW_FEDERATION_PROVIDERS(false),
VIEW_FEDERATION_PROVIDER(false),
CREATE_FEDERATION_PROVIDER(false),
UPDATE_FEDERATION_PROVIDER(false),
DELETE_FEDERATION_PROVIDER(false);
private boolean saveByDefault;
EventType(boolean saveByDefault) {
this.saveByDefault = saveByDefault;
}
public boolean isSaveByDefault() {
return saveByDefault;
}
IDENTITY_PROVIDER_LOGIN,
IDENTITY_PROVIDER_LOGIN_ERROR,
IDENTITY_PROVIDER_RESPONSE,
IDENTITY_PROVIDER_RESPONSE_ERROR,
IDENTITY_PROVIDER_RETRIEVE_TOKEN,
IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR,
IDENTITY_PROVIDER_ACCCOUNT_LINKING,
IDENTITY_PROVIDER_ACCCOUNT_LINKING_ERROR,
} }

View file

@ -3,6 +3,7 @@ package org.keycloak.events.jpa;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table; import javax.persistence.Table;
/** /**
@ -22,6 +23,9 @@ public class EventEntity {
@Column(name="TYPE") @Column(name="TYPE")
private String type; private String type;
@Column(name="EVENT_GROUP")
private String group;
@Column(name="REALM_ID") @Column(name="REALM_ID")
private String realmId; private String realmId;
@ -43,6 +47,10 @@ public class EventEntity {
@Column(name="DETAILS_JSON", length = 2550) @Column(name="DETAILS_JSON", length = 2550)
private String detailsJson; private String detailsJson;
@Column(name="REPRESENTATION")
@Lob
private String representation;
public String getId() { public String getId() {
return id; return id;
} }
@ -67,6 +75,14 @@ public class EventEntity {
this.type = type; this.type = type;
} }
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getRealmId() { public String getRealmId() {
return realmId; return realmId;
} }
@ -123,4 +139,11 @@ public class EventEntity {
this.detailsJson = detailsJson; this.detailsJson = detailsJson;
} }
public String getRepresentation() {
return representation;
}
public void setRepresentation(String representation) {
this.representation = representation;
}
} }

View file

@ -4,15 +4,15 @@ import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference; import org.codehaus.jackson.type.TypeReference;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.events.Event; import org.keycloak.events.Event;
import org.keycloak.events.EventGroup;
import org.keycloak.events.EventQuery; import org.keycloak.events.EventQuery;
import org.keycloak.events.EventStoreProvider; import org.keycloak.events.EventStoreProvider;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
/** /**
@ -26,12 +26,9 @@ public class JpaEventStoreProvider implements EventStoreProvider {
private static final Logger logger = Logger.getLogger(JpaEventStoreProvider.class); private static final Logger logger = Logger.getLogger(JpaEventStoreProvider.class);
private EntityManager em; private EntityManager em;
private EntityTransaction tx;
private Set<EventType> includedEvents;
public JpaEventStoreProvider(EntityManager em, Set<EventType> includedEvents) { public JpaEventStoreProvider(EntityManager em) {
this.em = em; this.em = em;
this.includedEvents = includedEvents;
} }
@Override @Override
@ -56,9 +53,7 @@ public class JpaEventStoreProvider implements EventStoreProvider {
@Override @Override
public void onEvent(Event event) { public void onEvent(Event event) {
if (includedEvents.contains(event.getType())) { em.persist(convert(event));
em.persist(convert(event));
}
} }
@Override @Override
@ -70,12 +65,14 @@ public class JpaEventStoreProvider implements EventStoreProvider {
e.setId(UUID.randomUUID().toString()); e.setId(UUID.randomUUID().toString());
e.setTime(o.getTime()); e.setTime(o.getTime());
e.setType(o.getType().toString()); e.setType(o.getType().toString());
e.setGroup(o.getGroup().toString());
e.setRealmId(o.getRealmId()); e.setRealmId(o.getRealmId());
e.setClientId(o.getClientId()); e.setClientId(o.getClientId());
e.setUserId(o.getUserId()); e.setUserId(o.getUserId());
e.setSessionId(o.getSessionId()); e.setSessionId(o.getSessionId());
e.setIpAddress(o.getIpAddress()); e.setIpAddress(o.getIpAddress());
e.setError(o.getError()); e.setError(o.getError());
e.setRepresentation(o.getRepresentation());
try { try {
e.setDetailsJson(mapper.writeValueAsString(o.getDetails())); e.setDetailsJson(mapper.writeValueAsString(o.getDetails()));
} catch (IOException ex) { } catch (IOException ex) {
@ -88,12 +85,14 @@ public class JpaEventStoreProvider implements EventStoreProvider {
Event e = new Event(); Event e = new Event();
e.setTime(o.getTime()); e.setTime(o.getTime());
e.setType(EventType.valueOf(o.getType())); e.setType(EventType.valueOf(o.getType()));
e.setGroup(EventGroup.valueOf(o.getGroup()));
e.setRealmId(o.getRealmId()); e.setRealmId(o.getRealmId());
e.setClientId(o.getClientId()); e.setClientId(o.getClientId());
e.setUserId(o.getUserId()); e.setUserId(o.getUserId());
e.setSessionId(o.getSessionId()); e.setSessionId(o.getSessionId());
e.setIpAddress(o.getIpAddress()); e.setIpAddress(o.getIpAddress());
e.setError(o.getError()); e.setError(o.getError());
e.setRepresentation(o.getRepresentation());
try { try {
Map<String, String> details = mapper.readValue(o.getDetailsJson(), mapType); Map<String, String> details = mapper.readValue(o.getDetailsJson(), mapType);
e.setDetails(details); e.setDetails(details);

View file

@ -18,33 +18,14 @@ public class JpaEventStoreProviderFactory implements EventStoreProviderFactory {
public static final String ID = "jpa"; public static final String ID = "jpa";
private Set<EventType> includedEvents = new HashSet<EventType>();
@Override @Override
public EventStoreProvider create(KeycloakSession session) { public EventStoreProvider create(KeycloakSession session) {
JpaConnectionProvider connection = session.getProvider(JpaConnectionProvider.class); JpaConnectionProvider connection = session.getProvider(JpaConnectionProvider.class);
return new JpaEventStoreProvider(connection.getEntityManager(), includedEvents); return new JpaEventStoreProvider(connection.getEntityManager());
} }
@Override @Override
public void init(Config.Scope config) { public void init(Config.Scope config) {
String[] include = config.getArray("include-events");
if (include != null) {
for (String i : include) {
includedEvents.add(EventType.valueOf(i.toUpperCase()));
}
} else {
for (EventType i : EventType.values()) {
includedEvents.add(i);
}
}
String[] exclude = config.getArray("exclude-events");
if (exclude != null) {
for (String e : exclude) {
includedEvents.remove(EventType.valueOf(e.toUpperCase()));
}
}
} }
@Override @Override

View file

@ -3,14 +3,15 @@ package org.keycloak.events.mongo;
import com.mongodb.BasicDBObject; import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection; import com.mongodb.DBCollection;
import com.mongodb.DBObject; import com.mongodb.DBObject;
import org.keycloak.events.Event; import org.keycloak.events.Event;
import org.keycloak.events.EventGroup;
import org.keycloak.events.EventQuery; import org.keycloak.events.EventQuery;
import org.keycloak.events.EventStoreProvider; import org.keycloak.events.EventStoreProvider;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -18,11 +19,9 @@ import java.util.Set;
public class MongoEventStoreProvider implements EventStoreProvider { public class MongoEventStoreProvider implements EventStoreProvider {
private DBCollection events; private DBCollection events;
private Set<EventType> includedEvents;
public MongoEventStoreProvider(DBCollection events, Set<EventType> includedEvents) { public MongoEventStoreProvider(DBCollection events) {
this.events = events; this.events = events;
this.includedEvents = includedEvents;
} }
@Override @Override
@ -50,9 +49,7 @@ public class MongoEventStoreProvider implements EventStoreProvider {
@Override @Override
public void onEvent(Event event) { public void onEvent(Event event) {
if (includedEvents.contains(event.getType())) { events.insert(convert(event));
events.insert(convert(event));
}
} }
@Override @Override
@ -63,12 +60,14 @@ public class MongoEventStoreProvider implements EventStoreProvider {
BasicDBObject e = new BasicDBObject(); BasicDBObject e = new BasicDBObject();
e.put("time", o.getTime()); e.put("time", o.getTime());
e.put("type", o.getType().toString()); e.put("type", o.getType().toString());
e.put("group", o.getGroup().toString());
e.put("realmId", o.getRealmId()); e.put("realmId", o.getRealmId());
e.put("clientId", o.getClientId()); e.put("clientId", o.getClientId());
e.put("userId", o.getUserId()); e.put("userId", o.getUserId());
e.put("sessionId", o.getSessionId()); e.put("sessionId", o.getSessionId());
e.put("ipAddress", o.getIpAddress()); e.put("ipAddress", o.getIpAddress());
e.put("error", o.getError()); e.put("error", o.getError());
e.put("representation", o.getRepresentation());
BasicDBObject details = new BasicDBObject(); BasicDBObject details = new BasicDBObject();
if (o.getDetails() != null) { if (o.getDetails() != null) {
@ -85,12 +84,14 @@ public class MongoEventStoreProvider implements EventStoreProvider {
Event e = new Event(); Event e = new Event();
e.setTime(o.getLong("time")); e.setTime(o.getLong("time"));
e.setType(EventType.valueOf(o.getString("type"))); e.setType(EventType.valueOf(o.getString("type")));
e.setGroup(EventGroup.valueOf(o.getString("group")));
e.setRealmId(o.getString("realmId")); e.setRealmId(o.getString("realmId"));
e.setClientId(o.getString("clientId")); e.setClientId(o.getString("clientId"));
e.setUserId(o.getString("userId")); e.setUserId(o.getString("userId"));
e.setSessionId(o.getString("sessionId")); e.setSessionId(o.getString("sessionId"));
e.setIpAddress(o.getString("ipAddress")); e.setIpAddress(o.getString("ipAddress"));
e.setError(o.getString("error")); e.setError(o.getString("error"));
e.setRepresentation(o.getString("representation"));
BasicDBObject d = (BasicDBObject) o.get("details"); BasicDBObject d = (BasicDBObject) o.get("details");
if (d != null) { if (d != null) {

View file

@ -23,8 +23,6 @@ public class MongoEventStoreProviderFactory implements EventStoreProviderFactory
public static final String ID = "mongo"; public static final String ID = "mongo";
private Set<EventType> includedEvents = new HashSet<EventType>();
@Override @Override
public EventStoreProvider create(KeycloakSession session) { public EventStoreProvider create(KeycloakSession session) {
MongoConnectionProvider connection = session.getProvider(MongoConnectionProvider.class); MongoConnectionProvider connection = session.getProvider(MongoConnectionProvider.class);
@ -32,28 +30,11 @@ public class MongoEventStoreProviderFactory implements EventStoreProviderFactory
DBCollection collection = connection.getDB().getCollection("events"); DBCollection collection = connection.getDB().getCollection("events");
collection.setWriteConcern(WriteConcern.UNACKNOWLEDGED); collection.setWriteConcern(WriteConcern.UNACKNOWLEDGED);
return new MongoEventStoreProvider(collection, includedEvents); return new MongoEventStoreProvider(collection);
} }
@Override @Override
public void init(Config.Scope config) { public void init(Config.Scope config) {
String[] include = config.getArray("include-events");
if (include != null) {
for (String i : include) {
includedEvents.add(EventType.valueOf(i.toUpperCase()));
}
} else {
for (EventType i : EventType.values()) {
includedEvents.add(i);
}
}
String[] exclude = config.getArray("exclude-events");
if (exclude != null) {
for (String e : exclude) {
includedEvents.remove(EventType.valueOf(e.toUpperCase()));
}
}
} }
@Override @Override

View file

@ -24,7 +24,7 @@ public class MemEventStoreProvider implements EventStoreProvider {
@Override @Override
public EventQuery createQuery() { public EventQuery createQuery() {
return new MemEventQuery(new LinkedList<Event>(events)); return new MemEventQuery(new LinkedList<>(events));
} }
@Override @Override
@ -59,7 +59,9 @@ public class MemEventStoreProvider implements EventStoreProvider {
@Override @Override
public void onEvent(Event event) { public void onEvent(Event event) {
events.add(0, event); if (!excludedEvents.contains(event.getType())) {
events.add(0, event);
}
} }
@Override @Override

View file

@ -34,7 +34,7 @@ public class MemEventStoreProviderFactory implements EventStoreProviderFactory {
String excludes = config.get("excludes"); String excludes = config.get("excludes");
if (excludes != null) { if (excludes != null) {
excludedEvents = new HashSet<EventType>(); excludedEvents = new HashSet<>();
for (String e : excludes.split(",")) { for (String e : excludes.split(",")) {
excludedEvents.add(EventType.valueOf(e)); excludedEvents.add(EventType.valueOf(e));
} }

View file

@ -244,6 +244,9 @@ module.config([ '$routeProvider', function($routeProvider) {
resolve : { resolve : {
realm : function(RealmLoader) { realm : function(RealmLoader) {
return RealmLoader(); return RealmLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
} }
}, },
controller : 'RealmEventsCtrl' controller : 'RealmEventsCtrl'

View file

@ -1239,6 +1239,12 @@ module.controller('RealmEventsConfigCtrl', function($scope, eventsConfig, RealmE
$scope.eventListeners = serverInfo.eventListeners; $scope.eventListeners = serverInfo.eventListeners;
$scope.eventSelectOptions = {
'multiple': true,
'simple_tags': true,
'tags': serverInfo.eventTypes
};
var oldCopy = angular.copy($scope.eventsConfig); var oldCopy = angular.copy($scope.eventsConfig);
$scope.changed = false; $scope.changed = false;
@ -1278,14 +1284,15 @@ module.controller('RealmEventsConfigCtrl', function($scope, eventsConfig, RealmE
}; };
}); });
module.controller('RealmEventsCtrl', function($scope, RealmEvents, realm) { module.controller('RealmEventsCtrl', function($scope, RealmEvents, realm, serverInfo) {
$scope.realm = realm; $scope.realm = realm;
$scope.page = 0; $scope.page = 0;
$scope.eventTypes = [{tag:'LOGIN'}, {tag:'REGISTER'}, {tag:'LOGOUT'}, {tag:'CODE_TO_TOKEN'}, {tag:'REFRESH_TOKEN'}, $scope.eventSelectOptions = {
{tag:'LOGIN_ERROR'}, {tag:'REGISTER_ERROR'}, {tag:'LOGOUT_ERROR'}, {tag:'CODE_TO_TOKEN_ERROR'}, {tag:'REFRESH_TOKEN_ERROR'}, 'multiple': true,
{tag:'VALIDATE_ACCESS_TOKEN'}, {tag:'VALIDATE_ACCESS_TOKEN_ERROR'}, {tag:'SOCIAL_LINK'}, {tag:'SOCIAL_LINK_ERROR'}, {tag:'REMOVE_FEDERATED_IDENTITY'}, 'simple_tags': true,
{tag:'REMOVE_SOCIAL_LINK_ERROR'}, {tag:'UPDATE_EMAIL'}, {tag:'UPDATE_PROFILE'}, {tag:'UPDATE_PASSWORD'}, {tag:'UPDATE_TOTP'}]; 'tags': serverInfo.eventTypes
};
$scope.query = { $scope.query = {
id : realm.realm, id : realm.realm,

View file

@ -18,6 +18,17 @@
</div> </div>
<span tooltip-placement="right" tooltip="If enabled events are saved to the database which makes events available to the admin and account management consoles." class="fa fa-info-circle"></span> <span tooltip-placement="right" tooltip="If enabled events are saved to the database which makes events available to the admin and account management consoles." class="fa fa-info-circle"></span>
</div> </div>
<div class="form-group" data-ng-show="eventsConfig.eventsEnabled">
<label class="col-sm-2 control-label" for="enabledEventTypes" class="control-label">Saved Types</label>
<div class="col-sm-5">
<input ui-select2="eventSelectOptions" id="enabledEventTypes" ng-model="eventsConfig.enabledEventTypes" data-placeholder="Select event types..."/>
</div>
<span tooltip-placement="right" tooltip="Configure what event types are saved. By default events related to login and users modifying their accounts are persisted." class="fa fa-info-circle"></span>
</div>
<div class="form-group" data-ng-show="access.manageEvents && eventsConfig.eventsEnabled"> <div class="form-group" data-ng-show="access.manageEvents && eventsConfig.eventsEnabled">
<label class="col-sm-2 control-label" for="password">Clear Events</label> <label class="col-sm-2 control-label" for="password">Clear Events</label>
<div class="col-sm-5"> <div class="col-sm-5">

View file

@ -31,13 +31,11 @@
</div> </div>
<form class="form-horizontal"> <form class="form-horizontal">
<div class="form-group" data-ng-show="filter"> <div class="form-group" data-ng-show="filter">
<label class="col-sm-2 control-label" for="eventType">Event Type</label> <label class="col-sm-2 control-label" for="eventTypes">Event Type</label>
<div class="col-sm-5"> <div class="col-sm-5">
<select ui-select2 id="reqActions" ng-model="query.type" data-placeholder="Select event types..." multiple> <input ui-select2="eventSelectOptions" id="eventTypes" ng-model="query.type" data-placeholder="Select event types..."/>
<option ng-repeat="event in eventTypes|orderBy:'tag'">{{event.tag}}</option> </div>
</select> </div>
</div>
</div>
<div class="form-group" data-ng-show="filter"> <div class="form-group" data-ng-show="filter">
<label class="col-sm-2 control-label" for="client">Client</label> <label class="col-sm-2 control-label" for="client">Client</label>
<div class="col-sm-4"> <div class="col-sm-4">
@ -58,7 +56,7 @@
</div> </div>
</div> </div>
<div class="form-group" data-ng-show="filter"> <div class="form-group" data-ng-show="filter">
<label class="col-sm-2 control-label" for="toDate">Date (To)</label> <label class="col-sm-2 control-label" for="dateTo">Date (To)</label>
<div class="col-sm-4"> <div class="col-sm-4">
<input class="form-control" type="date" id="dateTo" name="dateTo" data-ng-model="query.dateTo"> <input class="form-control" type="date" id="dateTo" name="dateTo" data-ng-model="query.dateTo">
</div> </div>
@ -109,6 +107,16 @@
</table> </table>
</td> </td>
</tr> </tr>
<tr data-ng-show="event.representation">
<td>Representation</td>
<td>
<button type="button" class="btn btn-default btn-xs" ng-click="collapseRep = !collapseRep">
<span class="glyphicon glyphicon-plus" data-ng-show="!collapseRep"></span>
<span class="glyphicon glyphicon-minus" data-ng-show="collapseRep"></span>
</button>
<pre data-ng-show="collapseRep">{{event.representation}}</pre>
</td>
</tr>
</table> </table>
</td> </td>
</tr> </tr>

View file

@ -10,6 +10,15 @@
<h2 class="pull-left">{{identityProvider.alias}} Provider Settings</h2> <h2 class="pull-left">{{identityProvider.alias}} Provider Settings</h2>
<p class="subtitle"><span class="required">*</span> Required fields</p> <p class="subtitle"><span class="required">*</span> Required fields</p>
<form class="form-horizontal" name="realmForm" novalidate> <form class="form-horizontal" name="realmForm" novalidate>
<fieldset>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="redirectUri">Redirect URI</label>
<div class="col-sm-6">
<input class="form-control" id="redirectUri" type="text" value="{{callbackUrl}}{{identityProvider.alias}}/endpoint" readonly kc-select-action="click">
</div>
<span tooltip-placement="right" tooltip="The redirect uri to use when configuring the identity provider" class="fa fa-info-circle"></span>
</div>
</fieldset>
<fieldset> <fieldset>
<div class="form-group clearfix"> <div class="form-group clearfix">
<label class="col-sm-2 control-label" for="identifier">Alias <span class="required">*</span></label> <label class="col-sm-2 control-label" for="identifier">Alias <span class="required">*</span></label>

View file

@ -10,6 +10,15 @@
<h2 class="pull-left">{{identityProvider.alias}} Provider Settings</h2> <h2 class="pull-left">{{identityProvider.alias}} Provider Settings</h2>
<p class="subtitle"><span class="required">*</span> Required fields</p> <p class="subtitle"><span class="required">*</span> Required fields</p>
<form class="form-horizontal" name="realmForm" novalidate> <form class="form-horizontal" name="realmForm" novalidate>
<fieldset>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="redirectUri">Redirect URI</label>
<div class="col-sm-6">
<input class="form-control" id="redirectUri" type="text" value="{{callbackUrl}}{{identityProvider.alias}}/endpoint" readonly kc-select-action="click">
</div>
<span tooltip-placement="right" tooltip="The redirect uri to use when configuring the identity provider" class="fa fa-info-circle"></span>
</div>
</fieldset>
<fieldset> <fieldset>
<div class="form-group clearfix"> <div class="form-group clearfix">
<label class="col-sm-2 control-label" for="identifier">Alias <span class="required">*</span></label> <label class="col-sm-2 control-label" for="identifier">Alias <span class="required">*</span></label>

View file

@ -10,6 +10,15 @@
<h2 class="pull-left">{{identityProvider.alias}} Provider Settings</h2> <h2 class="pull-left">{{identityProvider.alias}} Provider Settings</h2>
<p class="subtitle"><span class="required">*</span> Required fields</p> <p class="subtitle"><span class="required">*</span> Required fields</p>
<form class="form-horizontal" name="realmForm" novalidate> <form class="form-horizontal" name="realmForm" novalidate>
<fieldset>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="redirectUri">Redirect URI</label>
<div class="col-sm-6">
<input class="form-control" id="redirectUri" type="text" value="{{callbackUrl}}{{identityProvider.alias}}/endpoint" readonly kc-select-action="click">
</div>
<span tooltip-placement="right" tooltip="The redirect uri to use when configuring the identity provider" class="fa fa-info-circle"></span>
</div>
</fieldset>
<fieldset> <fieldset>
<div class="form-group clearfix"> <div class="form-group clearfix">
<label class="col-sm-2 control-label" for="clientId">Client ID <span class="required">*</span></label> <label class="col-sm-2 control-label" for="clientId">Client ID <span class="required">*</span></label>

View file

@ -26,7 +26,7 @@
</tr> </tr>
<tr ng-show="configuredProviders.length > 0"> <tr ng-show="configuredProviders.length > 0">
<th>Name</th> <th>Name</th>
<th>Callback URI</th> <th>Provider</th>
</tr> </tr>
</thead> </thead>
<tbody ng-show="configuredProviders.length > 0"> <tbody ng-show="configuredProviders.length > 0">
@ -34,9 +34,7 @@
<td> <td>
<a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">{{identityProvider.alias}}</a> <a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">{{identityProvider.alias}}</a>
</td> </td>
<td> <td>{{identityProvider.providerId}}</td>
{{callbackUrl}}{{identityProvider.alias}}/endpoint
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View file

@ -237,6 +237,10 @@ public interface RealmModel extends RoleContainerModel {
void setEventsListeners(Set<String> listeners); void setEventsListeners(Set<String> listeners);
Set<String> getEnabledEventTypes();
void setEnabledEventTypes(Set<String> enabledEventTypes);
ApplicationModel getMasterAdminApp(); ApplicationModel getMasterAdminApp();
void setMasterAdminApp(ApplicationModel app); void setMasterAdminApp(ApplicationModel app);

View file

@ -62,6 +62,7 @@ public class RealmEntity extends AbstractIdentifiableEntity {
private boolean eventsEnabled; private boolean eventsEnabled;
private long eventsExpiration; private long eventsExpiration;
private List<String> eventsListeners = new ArrayList<String>(); private List<String> eventsListeners = new ArrayList<String>();
private List<String> enabledEventTypes = new ArrayList<String>();
private String adminAppId; private String adminAppId;
@ -380,6 +381,14 @@ public class RealmEntity extends AbstractIdentifiableEntity {
this.eventsListeners = eventsListeners; this.eventsListeners = eventsListeners;
} }
public List<String> getEnabledEventTypes() {
return enabledEventTypes;
}
public void setEnabledEventTypes(List<String> enabledEventTypes) {
this.enabledEventTypes = enabledEventTypes;
}
public String getAdminAppId() { public String getAdminAppId() {
return adminAppId; return adminAppId;
} }

View file

@ -116,6 +116,9 @@ public class ModelToRepresentation {
if (realm.getEventsListeners() != null) { if (realm.getEventsListeners() != null) {
rep.setEventsListeners(new LinkedList<String>(realm.getEventsListeners())); rep.setEventsListeners(new LinkedList<String>(realm.getEventsListeners()));
} }
if (realm.getEnabledEventTypes() != null) {
rep.setEnabledEventTypes(new LinkedList<String>(realm.getEnabledEventTypes()));
}
rep.setVerifyEmail(realm.isVerifyEmail()); rep.setVerifyEmail(realm.isVerifyEmail());
rep.setResetPasswordAllowed(realm.isResetPasswordAllowed()); rep.setResetPasswordAllowed(realm.isResetPasswordAllowed());
@ -182,6 +185,11 @@ public class ModelToRepresentation {
if (realm.getEventsListeners() != null) { if (realm.getEventsListeners() != null) {
rep.setEventsListeners(new LinkedList<String>(realm.getEventsListeners())); rep.setEventsListeners(new LinkedList<String>(realm.getEventsListeners()));
} }
if(realm.getEnabledEventTypes() != null) {
rep.setEnabledEventTypes(new LinkedList<String>(realm.getEnabledEventTypes()));
}
return rep; return rep;
} }

View file

@ -357,6 +357,7 @@ public class RepresentationToModel {
if (rep.isEventsEnabled() != null) realm.setEventsEnabled(rep.isEventsEnabled()); if (rep.isEventsEnabled() != null) realm.setEventsEnabled(rep.isEventsEnabled());
if (rep.getEventsExpiration() != null) realm.setEventsExpiration(rep.getEventsExpiration()); if (rep.getEventsExpiration() != null) realm.setEventsExpiration(rep.getEventsExpiration());
if (rep.getEventsListeners() != null) realm.setEventsListeners(new HashSet<String>(rep.getEventsListeners())); if (rep.getEventsListeners() != null) realm.setEventsListeners(new HashSet<String>(rep.getEventsListeners()));
if (rep.getEnabledEventTypes() != null) realm.setEnabledEventTypes(new HashSet<String>(rep.getEnabledEventTypes()));
if (rep.getPasswordPolicy() != null) realm.setPasswordPolicy(new PasswordPolicy(rep.getPasswordPolicy())); if (rep.getPasswordPolicy() != null) realm.setPasswordPolicy(new PasswordPolicy(rep.getPasswordPolicy()));

View file

@ -1039,6 +1039,20 @@ public class RealmAdapter implements RealmModel {
} }
} }
@Override
public Set<String> getEnabledEventTypes() {
return new HashSet<String>(realm.getEnabledEventTypes());
}
@Override
public void setEnabledEventTypes(Set<String> enabledEventTypes) {
if (enabledEventTypes != null) {
realm.setEnabledEventTypes(new ArrayList<String>(enabledEventTypes));
} else {
realm.setEnabledEventTypes(Collections.EMPTY_LIST);
}
}
@Override @Override
public ApplicationModel getMasterAdminApp() { public ApplicationModel getMasterAdminApp() {
return this.masterAdminApp; return this.masterAdminApp;

View file

@ -806,6 +806,18 @@ public class RealmAdapter implements RealmModel {
updated.setEventsListeners(listeners); updated.setEventsListeners(listeners);
} }
@Override
public Set<String> getEnabledEventTypes() {
if (updated != null) return updated.getEnabledEventTypes();
return cached.getEnabledEventTypes();
}
@Override
public void setEnabledEventTypes(Set<String> enabledEventTypes) {
getDelegateForUpdate();
updated.setEnabledEventTypes(enabledEventTypes);
}
@Override @Override
public ApplicationModel getMasterAdminApp() { public ApplicationModel getMasterAdminApp() {
return cacheSession.getRealm(Config.getAdminRealm()).getApplicationById(cached.getMasterAdminApp()); return cacheSession.getRealm(Config.getAdminRealm()).getApplicationById(cached.getMasterAdminApp());

View file

@ -79,6 +79,7 @@ public class CachedRealm {
private boolean eventsEnabled; private boolean eventsEnabled;
private long eventsExpiration; private long eventsExpiration;
private Set<String> eventsListeners = new HashSet<String>(); private Set<String> eventsListeners = new HashSet<String>();
private Set<String> enabledEventTypes = new HashSet<String>();
private List<String> defaultRoles = new LinkedList<String>(); private List<String> defaultRoles = new LinkedList<String>();
private Map<String, String> realmRoles = new HashMap<String, String>(); private Map<String, String> realmRoles = new HashMap<String, String>();
private Map<String, String> applications = new HashMap<String, String>(); private Map<String, String> applications = new HashMap<String, String>();
@ -146,6 +147,7 @@ public class CachedRealm {
eventsEnabled = model.isEventsEnabled(); eventsEnabled = model.isEventsEnabled();
eventsExpiration = model.getEventsExpiration(); eventsExpiration = model.getEventsExpiration();
eventsListeners.addAll(model.getEventsListeners()); eventsListeners.addAll(model.getEventsListeners());
enabledEventTypes.addAll(model.getEnabledEventTypes());
defaultRoles.addAll(model.getDefaultRoles()); defaultRoles.addAll(model.getDefaultRoles());
masterAdminApp = model.getMasterAdminApp().getId(); masterAdminApp = model.getMasterAdminApp().getId();
@ -349,6 +351,10 @@ public class CachedRealm {
return eventsListeners; return eventsListeners;
} }
public Set<String> getEnabledEventTypes() {
return enabledEventTypes;
}
public List<UserFederationProviderModel> getUserFederationProviders() { public List<UserFederationProviderModel> getUserFederationProviders() {
return userFederationProviders; return userFederationProviders;
} }

View file

@ -1140,6 +1140,17 @@ public class RealmAdapter implements RealmModel {
em.flush(); em.flush();
} }
@Override
public Set<String> getEnabledEventTypes() {
return realm.getEnabledEventTypes();
}
@Override
public void setEnabledEventTypes(Set<String> enabledEventTypes) {
realm.setEnabledEventTypes(enabledEventTypes);
em.flush();
}
@Override @Override
public ApplicationModel getMasterAdminApp() { public ApplicationModel getMasterAdminApp() {
return new ApplicationAdapter(this, em, session, realm.getMasterAdminApp()); return new ApplicationAdapter(this, em, session, realm.getMasterAdminApp());

View file

@ -130,6 +130,11 @@ public class RealmEntity {
@CollectionTable(name="REALM_EVENTS_LISTENERS", joinColumns={ @JoinColumn(name="REALM_ID") }) @CollectionTable(name="REALM_EVENTS_LISTENERS", joinColumns={ @JoinColumn(name="REALM_ID") })
protected Set<String> eventsListeners = new HashSet<String>(); protected Set<String> eventsListeners = new HashSet<String>();
@ElementCollection
@Column(name="VALUE")
@CollectionTable(name="REALM_ENABLED_EVENT_TYPES", joinColumns={ @JoinColumn(name="REALM_ID") })
protected Set<String> enabledEventTypes = new HashSet<String>();
@OneToOne @OneToOne
@JoinColumn(name="MASTER_ADMIN_APP") @JoinColumn(name="MASTER_ADMIN_APP")
protected ApplicationEntity masterAdminApp; protected ApplicationEntity masterAdminApp;
@ -419,6 +424,14 @@ public class RealmEntity {
this.eventsListeners = eventsListeners; this.eventsListeners = eventsListeners;
} }
public Set<String> getEnabledEventTypes() {
return enabledEventTypes;
}
public void setEnabledEventTypes(Set<String> enabledEventTypes) {
this.enabledEventTypes = enabledEventTypes;
}
public ApplicationEntity getMasterAdminApp() { public ApplicationEntity getMasterAdminApp() {
return masterAdminApp; return masterAdminApp;
} }

View file

@ -1045,6 +1045,21 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
updateRealm(); updateRealm();
} }
@Override
public Set<String> getEnabledEventTypes() {
return new HashSet<String>(realm.getEnabledEventTypes());
}
@Override
public void setEnabledEventTypes(Set<String> enabledEventTypes) {
if (enabledEventTypes != null) {
realm.setEnabledEventTypes(new ArrayList<String>(enabledEventTypes));
} else {
realm.setEnabledEventTypes(Collections.EMPTY_LIST);
}
updateRealm();
}
@Override @Override
public ApplicationModel getMasterAdminApp() { public ApplicationModel getMasterAdminApp() {
MongoApplicationEntity appData = getMongoStore().loadEntity(MongoApplicationEntity.class, realm.getAdminAppId(), invocationContext); MongoApplicationEntity appData = getMongoStore().loadEntity(MongoApplicationEntity.class, realm.getAdminAppId(), invocationContext);

View file

@ -1,18 +1,9 @@
package org.keycloak.protocol.oidc; package org.keycloak.protocol.oidc;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.ClientConnection;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException;
import org.keycloak.RSATokenVerifier;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder; import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.jose.jwk.JWK; import org.keycloak.jose.jwk.JWK;
import org.keycloak.jose.jwk.JWKBuilder; import org.keycloak.jose.jwk.JWKBuilder;
import org.keycloak.login.LoginFormsProvider; import org.keycloak.login.LoginFormsProvider;
@ -25,8 +16,6 @@ import org.keycloak.protocol.oidc.endpoints.TokenEndpoint;
import org.keycloak.protocol.oidc.endpoints.UserInfoEndpoint; import org.keycloak.protocol.oidc.endpoints.UserInfoEndpoint;
import org.keycloak.protocol.oidc.endpoints.ValidateTokenEndpoint; import org.keycloak.protocol.oidc.endpoints.ValidateTokenEndpoint;
import org.keycloak.protocol.oidc.representations.JSONWebKeySet; import org.keycloak.protocol.oidc.representations.JSONWebKeySet;
import org.keycloak.representations.AccessToken;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.resources.RealmsResource; import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.resources.flows.Flows; import org.keycloak.services.resources.flows.Flows;
@ -39,12 +28,8 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Providers;
import java.util.HashMap;
import java.util.Map;
/** /**
* Resource class for the oauth/openid connect token service * Resource class for the oauth/openid connect token service

View file

@ -20,12 +20,12 @@ package org.keycloak.protocol.oidc.endpoints;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse; import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.UnauthorizedException;
import org.keycloak.ClientConnection; import org.keycloak.ClientConnection;
import org.keycloak.OAuthErrorException; import org.keycloak.OAuthErrorException;
import org.keycloak.RSATokenVerifier; import org.keycloak.RSATokenVerifier;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.EventBuilder; import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventGroup;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
@ -36,8 +36,6 @@ import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.services.ErrorResponseException; import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.managers.AppAuthManager; import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.EventsManager;
import org.keycloak.services.resources.Cors; import org.keycloak.services.resources.Cors;
import org.keycloak.services.resources.flows.Urls; import org.keycloak.services.resources.flows.Urls;
@ -113,7 +111,7 @@ public class UserInfoEndpoint {
} }
private Response issueUserInfo(String tokenString) { private Response issueUserInfo(String tokenString) {
EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder() EventBuilder event = new EventBuilder(EventGroup.USER, realm, session, clientConnection)
.event(EventType.USER_INFO_REQUEST) .event(EventType.USER_INFO_REQUEST)
.detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN); .detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN);

View file

@ -9,6 +9,7 @@ import org.keycloak.VerificationException;
import org.keycloak.broker.provider.IdentityProvider; import org.keycloak.broker.provider.IdentityProvider;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.EventBuilder; import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventGroup;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.jose.jws.JWSBuilder; import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.login.LoginFormsProvider; import org.keycloak.login.LoginFormsProvider;
@ -181,7 +182,7 @@ public class AuthenticationManager {
expireRememberMeCookie(realm, uriInfo, connection); expireRememberMeCookie(realm, uriInfo, connection);
userSession.setState(UserSessionModel.State.LOGGED_OUT); userSession.setState(UserSessionModel.State.LOGGED_OUT);
String method = userSession.getNote(KEYCLOAK_LOGOUT_PROTOCOL); String method = userSession.getNote(KEYCLOAK_LOGOUT_PROTOCOL);
EventBuilder event = new EventsManager(realm, session, connection).createEventBuilder(); EventBuilder event = new EventBuilder(EventGroup.USER, realm, session, connection);
LoginProtocol protocol = session.getProvider(LoginProtocol.class, method); LoginProtocol protocol = session.getProvider(LoginProtocol.class, method);
protocol.setRealm(realm) protocol.setRealm(realm)
.setHttpHeaders(headers) .setHttpHeaders(headers)

View file

@ -1,57 +0,0 @@
package org.keycloak.services.managers;
import org.jboss.logging.Logger;
import org.keycloak.ClientConnection;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.EventStoreProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class EventsManager {
private Logger log = Logger.getLogger(EventsManager.class);
private RealmModel realm;
private KeycloakSession session;
private ClientConnection clientConnection;
public EventsManager(RealmModel realm, KeycloakSession session, ClientConnection clientConnection) {
this.realm = realm;
this.session = session;
this.clientConnection = clientConnection;
}
public EventBuilder createEventBuilder() {
List<EventListenerProvider> listeners = new LinkedList<EventListenerProvider>();
if (realm.isEventsEnabled()) {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
if (eventStore != null) {
listeners.add(eventStore);
} else {
log.error("Events enabled, but no event store provider configured");
}
}
if (realm.getEventsListeners() != null) {
for (String id : realm.getEventsListeners()) {
EventListenerProvider listener = session.getProvider(EventListenerProvider.class, id);
if (listener != null) {
listeners.add(listener);
} else {
log.error("Event listener '" + id + "' registered, but provider not found");
}
}
}
return new EventBuilder(listeners, realm, clientConnection.getRemoteAddr());
}
}

View file

@ -25,6 +25,7 @@ import org.keycloak.timer.TimerProvider;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList;
import java.util.List; import java.util.List;
/** /**
@ -161,6 +162,9 @@ public class RealmManager {
if (rep.getEventsListeners() != null) { if (rep.getEventsListeners() != null) {
realm.setEventsListeners(new HashSet<String>(rep.getEventsListeners())); realm.setEventsListeners(new HashSet<String>(rep.getEventsListeners()));
} }
if(rep.getEnabledEventTypes() != null) {
realm.setEnabledEventTypes(new HashSet<String>(rep.getEnabledEventTypes()));
}
} }
// Should be RealmManager moved to model/api instead of referencing methods this way? // Should be RealmManager moved to model/api instead of referencing methods this way?

View file

@ -25,7 +25,6 @@ import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.BadRequestException; import org.jboss.resteasy.spi.BadRequestException;
import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection; import org.keycloak.ClientConnection;
import org.keycloak.OAuth2Constants;
import org.keycloak.account.AccountPages; import org.keycloak.account.AccountPages;
import org.keycloak.account.AccountProvider; import org.keycloak.account.AccountProvider;
import org.keycloak.events.Details; import org.keycloak.events.Details;
@ -46,7 +45,6 @@ import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.Auth; import org.keycloak.services.managers.Auth;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.messages.Messages; import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.flows.Flows; import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.resources.flows.OAuthRedirect; import org.keycloak.services.resources.flows.OAuthRedirect;
@ -98,7 +96,7 @@ public class AccountService {
} }
private static final EventType[] LOG_EVENTS = {EventType.LOGIN, EventType.LOGOUT, EventType.REGISTER, EventType.REMOVE_FEDERATED_IDENTITY, EventType.REMOVE_TOTP, EventType.SEND_RESET_PASSWORD, private static final EventType[] LOG_EVENTS = {EventType.LOGIN, EventType.LOGOUT, EventType.REGISTER, EventType.REMOVE_FEDERATED_IDENTITY, EventType.REMOVE_TOTP, EventType.SEND_RESET_PASSWORD,
EventType.SEND_VERIFY_EMAIL, EventType.SOCIAL_LINK, EventType.UPDATE_EMAIL, EventType.UPDATE_PASSWORD, EventType.UPDATE_PROFILE, EventType.UPDATE_TOTP, EventType.VERIFY_EMAIL}; EventType.SEND_VERIFY_EMAIL, EventType.FEDERATED_IDENTITY_LINK, EventType.UPDATE_EMAIL, EventType.UPDATE_PASSWORD, EventType.UPDATE_PROFILE, EventType.UPDATE_TOTP, EventType.VERIFY_EMAIL};
private static final Set<String> LOG_DETAILS = new HashSet<String>(); private static final Set<String> LOG_DETAILS = new HashSet<String>();
static { static {

View file

@ -29,6 +29,7 @@ import org.keycloak.broker.provider.IdentityProviderFactory;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.Errors; import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder; import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventGroup;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel; import org.keycloak.models.ClientSessionModel;
@ -45,7 +46,6 @@ import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.AuthenticationManager.AuthResult; import org.keycloak.services.managers.AuthenticationManager.AuthResult;
import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.EventsManager;
import org.keycloak.services.messages.Messages; import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.flows.Flows; import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.resources.flows.Urls; import org.keycloak.services.resources.flows.Urls;
@ -109,7 +109,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
} }
public void init() { public void init() {
this.event = new EventsManager(this.realmModel, this.session, this.clientConnection).createEventBuilder().event(EventType.IDENTITY_PROVIDER_LOGIN); this.event = new EventBuilder(EventGroup.USER, realmModel, session, clientConnection).event(EventType.IDENTITY_PROVIDER_LOGIN);
} }
@GET @GET
@ -254,7 +254,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
this.event.event(EventType.IDENTITY_PROVIDER_LOGIN) this.event.event(EventType.IDENTITY_PROVIDER_LOGIN)
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri()) .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
.detail(Details.IDENTITY_PROVIDER_IDENTITY, federatedIdentity.getUsername()); .detail(Details.IDENTITY_PROVIDER_USERNAME, federatedIdentity.getUsername());
UserModel federatedUser = this.session.users().getUserByFederatedIdentity(federatedIdentityModel, this.realmModel); UserModel federatedUser = this.session.users().getUserByFederatedIdentity(federatedIdentityModel, this.realmModel);
@ -514,7 +514,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
this.event.clone().user(federatedUser).event(EventType.REGISTER) this.event.clone().user(federatedUser).event(EventType.REGISTER)
.detail(Details.IDENTITY_PROVIDER, federatedIdentityModel.getIdentityProvider()) .detail(Details.IDENTITY_PROVIDER, federatedIdentityModel.getIdentityProvider())
.detail(Details.IDENTITY_PROVIDER_IDENTITY, updatedIdentity.getUsername()) .detail(Details.IDENTITY_PROVIDER_USERNAME, updatedIdentity.getUsername())
.removeDetail("auth_method") .removeDetail("auth_method")
.success(); .success();

View file

@ -29,6 +29,7 @@ import org.keycloak.email.EmailProvider;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.Errors; import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder; import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventGroup;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.jose.jws.JWSBuilder; import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.login.LoginFormsProvider; import org.keycloak.login.LoginFormsProvider;
@ -537,7 +538,7 @@ public class LoginActionsService {
AttributeFormDataProcessor.process(formData, realm, user); AttributeFormDataProcessor.process(formData, realm, user);
event.user(user).success(); event.user(user).success();
event.reset(); event = new EventBuilder(EventGroup.USER, realm, session, clientConnection);
return processLogin(code, formData); return processLogin(code, formData);
} }

View file

@ -5,6 +5,7 @@ import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.ClientConnection; import org.keycloak.ClientConnection;
import org.keycloak.events.EventBuilder; import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventGroup;
import org.keycloak.models.ApplicationModel; import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Constants; import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
@ -15,7 +16,6 @@ import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService; import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.BruteForceProtector; import org.keycloak.services.managers.BruteForceProtector;
import org.keycloak.services.managers.EventsManager;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
import org.keycloak.wellknown.WellKnownProvider; import org.keycloak.wellknown.WellKnownProvider;
@ -91,7 +91,7 @@ public class RealmsResource {
// backward compatibility // backward compatibility
RealmManager realmManager = new RealmManager(session); RealmManager realmManager = new RealmManager(session);
RealmModel realm = locateRealm(name, realmManager); RealmModel realm = locateRealm(name, realmManager);
EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder(); EventBuilder event = new EventBuilder(EventGroup.USER, realm, session, clientConnection);
AuthenticationManager authManager = new AuthenticationManager(protector); AuthenticationManager authManager = new AuthenticationManager(protector);
LoginProtocolFactory factory = (LoginProtocolFactory)session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, OIDCLoginProtocol.LOGIN_PROTOCOL); LoginProtocolFactory factory = (LoginProtocolFactory)session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, OIDCLoginProtocol.LOGIN_PROTOCOL);
@ -107,7 +107,7 @@ public class RealmsResource {
final @PathParam("protocol") String protocol) { final @PathParam("protocol") String protocol) {
RealmManager realmManager = new RealmManager(session); RealmManager realmManager = new RealmManager(session);
RealmModel realm = locateRealm(name, realmManager); RealmModel realm = locateRealm(name, realmManager);
EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder(); EventBuilder event = new EventBuilder(EventGroup.USER, realm, session, clientConnection);
AuthenticationManager authManager = new AuthenticationManager(protector); AuthenticationManager authManager = new AuthenticationManager(protector);
LoginProtocolFactory factory = (LoginProtocolFactory)session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, protocol); LoginProtocolFactory factory = (LoginProtocolFactory)session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, protocol);
@ -129,7 +129,7 @@ public class RealmsResource {
public LoginActionsService getLoginActionsService(final @PathParam("realm") String name) { public LoginActionsService getLoginActionsService(final @PathParam("realm") String name) {
RealmManager realmManager = new RealmManager(session); RealmManager realmManager = new RealmManager(session);
RealmModel realm = locateRealm(name, realmManager); RealmModel realm = locateRealm(name, realmManager);
EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder(); EventBuilder event = new EventBuilder(EventGroup.USER, realm, session, clientConnection);
AuthenticationManager authManager = new AuthenticationManager(protector); AuthenticationManager authManager = new AuthenticationManager(protector);
LoginActionsService service = new LoginActionsService(realm, authManager, event); LoginActionsService service = new LoginActionsService(realm, authManager, event);
ResteasyProviderFactory.getInstance().injectProperties(service); ResteasyProviderFactory.getInstance().injectProperties(service);
@ -142,7 +142,7 @@ public class RealmsResource {
public ClientsManagementService getClientsManagementService(final @PathParam("realm") String name) { public ClientsManagementService getClientsManagementService(final @PathParam("realm") String name) {
RealmManager realmManager = new RealmManager(session); RealmManager realmManager = new RealmManager(session);
RealmModel realm = locateRealm(name, realmManager); RealmModel realm = locateRealm(name, realmManager);
EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder(); EventBuilder event = new EventBuilder(EventGroup.USER, realm, session, clientConnection);
ClientsManagementService service = new ClientsManagementService(realm, event); ClientsManagementService service = new ClientsManagementService(realm, event);
ResteasyProviderFactory.getInstance().injectProperties(service); ResteasyProviderFactory.getInstance().injectProperties(service);
return service; return service;
@ -168,7 +168,7 @@ public class RealmsResource {
throw new NotFoundException("account management not enabled"); throw new NotFoundException("account management not enabled");
} }
EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder(); EventBuilder event = new EventBuilder(EventGroup.USER, realm, session, clientConnection);
AccountService accountService = new AccountService(realm, application, event); AccountService accountService = new AccountService(realm, application, event);
ResteasyProviderFactory.getInstance().injectProperties(accountService); ResteasyProviderFactory.getInstance().injectProperties(accountService);
//resourceContext.initResource(accountService); //resourceContext.initResource(accountService);

View file

@ -7,7 +7,6 @@ import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse; import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.NotFoundException;
import org.keycloak.ClientConnection; import org.keycloak.ClientConnection;
import org.keycloak.Config;
import org.keycloak.Version; import org.keycloak.Version;
import org.keycloak.freemarker.BrowserSecurityHeaderSetup; import org.keycloak.freemarker.BrowserSecurityHeaderSetup;
import org.keycloak.freemarker.FreeMarkerException; import org.keycloak.freemarker.FreeMarkerException;
@ -28,13 +27,10 @@ import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.services.resources.flows.Urls; import org.keycloak.services.resources.flows.Urls;
import org.keycloak.util.MimeTypeUtil;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
@ -42,7 +38,6 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Providers; import javax.ws.rs.ext.Providers;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.HashMap; import java.util.HashMap;
@ -73,11 +68,6 @@ public class AdminConsole {
@Context @Context
protected KeycloakSession session; protected KeycloakSession session;
/*
@Context
protected ResourceContext resourceContext;
*/
@Context @Context
protected Providers providers; protected Providers providers;

View file

@ -8,6 +8,8 @@ import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.jboss.resteasy.spi.UnauthorizedException; import org.jboss.resteasy.spi.UnauthorizedException;
import org.keycloak.ClientConnection; import org.keycloak.ClientConnection;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventGroup;
import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.JWSInput;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
@ -27,6 +29,7 @@ import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.io.IOException; import java.io.IOException;
/** /**
@ -70,9 +73,6 @@ public class AdminRoot {
return base.path(AdminRoot.class); return base.path(AdminRoot.class);
} }
/** /**
* Convenience path to master realm admin console * Convenience path to master realm admin console
* *
@ -187,9 +187,11 @@ public class AdminRoot {
Cors.add(request).allowedOrigins(auth.getToken()).allowedMethods("GET", "PUT", "POST", "DELETE").auth().build(response); Cors.add(request).allowedOrigins(auth.getToken()).allowedMethods("GET", "PUT", "POST", "DELETE").auth().build(response);
RealmsAdminResource adminResource = new RealmsAdminResource(auth, tokenManager); EventBuilder event = new EventBuilder(EventGroup.ADMIN, auth.getRealm(), session, clientConnection);
event.user(auth.getUser()).client(auth.getClient());
RealmsAdminResource adminResource = new RealmsAdminResource(auth, tokenManager, event);
ResteasyProviderFactory.getInstance().injectProperties(adminResource); ResteasyProviderFactory.getInstance().injectProperties(adminResource);
//resourceContext.initResource(adminResource);
return adminResource; return adminResource;
} }
@ -211,7 +213,6 @@ public class AdminRoot {
ServerInfoAdminResource adminResource = new ServerInfoAdminResource(); ServerInfoAdminResource adminResource = new ServerInfoAdminResource();
ResteasyProviderFactory.getInstance().injectProperties(adminResource); ResteasyProviderFactory.getInstance().injectProperties(adminResource);
//resourceContext.initResource(adminResource);
return adminResource; return adminResource;
} }

View file

@ -5,6 +5,9 @@ import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.BadRequestException; import org.jboss.resteasy.spi.BadRequestException;
import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.events.Details;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.ApplicationModel; import org.keycloak.models.ApplicationModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
@ -40,6 +43,7 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -57,8 +61,10 @@ public class ApplicationResource {
protected static final Logger logger = Logger.getLogger(ApplicationResource.class); protected static final Logger logger = Logger.getLogger(ApplicationResource.class);
protected RealmModel realm; protected RealmModel realm;
private RealmAuth auth; private RealmAuth auth;
private EventBuilder event;
protected ApplicationModel application; protected ApplicationModel application;
protected KeycloakSession session; protected KeycloakSession session;
@Context @Context
protected UriInfo uriInfo; protected UriInfo uriInfo;
@ -69,11 +75,12 @@ public class ApplicationResource {
return (KeycloakApplication)keycloak; return (KeycloakApplication)keycloak;
} }
public ApplicationResource(RealmModel realm, RealmAuth auth, ApplicationModel applicationModel, KeycloakSession session) { public ApplicationResource(RealmModel realm, RealmAuth auth, ApplicationModel applicationModel, KeycloakSession session, EventBuilder event) {
this.realm = realm; this.realm = realm;
this.auth = auth; this.auth = auth;
this.application = applicationModel; this.application = applicationModel;
this.session = session; this.session = session;
this.event = event;
auth.init(RealmAuth.Resource.APPLICATION); auth.init(RealmAuth.Resource.APPLICATION);
} }
@ -98,6 +105,9 @@ public class ApplicationResource {
try { try {
RepresentationToModel.updateApplication(rep, application); RepresentationToModel.updateApplication(rep, application);
event.event(EventType.UPDATE_APPLICATION).representation(rep).success();
return Response.noContent().build(); return Response.noContent().build();
} catch (ModelDuplicateException e) { } catch (ModelDuplicateException e) {
return Flows.errors().exists("Application " + rep.getName() + " already exists"); return Flows.errors().exists("Application " + rep.getName() + " already exists");
@ -116,7 +126,11 @@ public class ApplicationResource {
public ApplicationRepresentation getApplication() { public ApplicationRepresentation getApplication() {
auth.requireView(); auth.requireView();
return ModelToRepresentation.toRepresentation(application); ApplicationRepresentation rep = ModelToRepresentation.toRepresentation(application);
event.event(EventType.VIEW_APPLICATION).representation(rep).success();
return rep;
} }
/** /**
@ -126,7 +140,7 @@ public class ApplicationResource {
*/ */
@Path("certificates/{attr}") @Path("certificates/{attr}")
public ClientAttributeCertificateResource getCertficateResource(@PathParam("attr") String attributePrefix) { public ClientAttributeCertificateResource getCertficateResource(@PathParam("attr") String attributePrefix) {
return new ClientAttributeCertificateResource(realm, auth, application, session, attributePrefix); return new ClientAttributeCertificateResource(realm, auth, application, session, attributePrefix, event);
} }
@ -176,6 +190,9 @@ public class ApplicationResource {
public void deleteApplication() { public void deleteApplication() {
auth.requireManage(); auth.requireManage();
ApplicationRepresentation rep = getApplication();
event.event(EventType.DELETE_APPLICATION).representation(rep).success();
new ApplicationManager(new RealmManager(session)).removeApplication(realm, application); new ApplicationManager(new RealmManager(session)).removeApplication(realm, application);
} }
@ -228,7 +245,7 @@ public class ApplicationResource {
@Path("roles") @Path("roles")
public RoleContainerResource getRoleContainerResource() { public RoleContainerResource getRoleContainerResource() {
return new RoleContainerResource(realm, auth, application); return new RoleContainerResource(realm, auth, application, event);
} }
/** /**
@ -331,6 +348,9 @@ public class ApplicationResource {
UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(userSession); UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(userSession);
sessions.add(rep); sessions.add(rep);
} }
event.event(EventType.VIEW_APPLICATION_USER_SESSIONS).representation(sessions).success();
return sessions; return sessions;
} }
@ -342,6 +362,9 @@ public class ApplicationResource {
@POST @POST
public GlobalRequestResult logoutAll() { public GlobalRequestResult logoutAll() {
auth.requireManage(); auth.requireManage();
event.event(EventType.LOGOUT_APPLICATION_USERS).success();
return new ResourceAdminManager().logoutApplication(uriInfo.getRequestUri(), realm, application); return new ResourceAdminManager().logoutApplication(uriInfo.getRequestUri(), realm, application);
} }
@ -358,6 +381,8 @@ public class ApplicationResource {
throw new NotFoundException("User not found"); throw new NotFoundException("User not found");
} }
event.event(EventType.LOGOUT_USER).success();
new ResourceAdminManager().logoutUserFromApplication(uriInfo.getRequestUri(), realm, application, user, session); new ResourceAdminManager().logoutUserFromApplication(uriInfo.getRequestUri(), realm, application, user, session);
} }
@ -378,6 +403,10 @@ public class ApplicationResource {
} }
if (logger.isDebugEnabled()) logger.debug("Register node: " + node); if (logger.isDebugEnabled()) logger.debug("Register node: " + node);
application.registerNode(node, Time.currentTime()); application.registerNode(node, Time.currentTime());
event.event(EventType.REGISTER_APPLICATION_CLUSTER_NODE)
.detail(Details.APPLICATION_CLUSTER_NODE, node)
.success();
} }
/** /**
@ -398,6 +427,10 @@ public class ApplicationResource {
} }
application.unregisterNode(node); application.unregisterNode(node);
event.event(EventType.UNREGISTER_APPLICATION_CLUSTER_NODE)
.detail(Details.APPLICATION_CLUSTER_NODE, node)
.success();
} }
/** /**

View file

@ -1,5 +1,6 @@
package org.keycloak.services.resources.admin; package org.keycloak.services.resources.admin;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ApplicationModel; import org.keycloak.models.ApplicationModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
@ -8,8 +9,8 @@ import org.keycloak.models.RealmModel;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class ApplicationsByIdResource extends ApplicationsResource { public class ApplicationsByIdResource extends ApplicationsResource {
public ApplicationsByIdResource(RealmModel realm, RealmAuth auth) { public ApplicationsByIdResource(RealmModel realm, RealmAuth auth, EventBuilder event) {
super(realm, auth); super(realm, auth, event);
} }
@Override @Override

View file

@ -4,6 +4,8 @@ import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.ApplicationModel; import org.keycloak.models.ApplicationModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
@ -23,6 +25,7 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -36,13 +39,15 @@ public class ApplicationsResource {
protected static final Logger logger = Logger.getLogger(RealmAdminResource.class); protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
protected RealmModel realm; protected RealmModel realm;
private RealmAuth auth; private RealmAuth auth;
private EventBuilder event;
@Context @Context
protected KeycloakSession session; protected KeycloakSession session;
public ApplicationsResource(RealmModel realm, RealmAuth auth) { public ApplicationsResource(RealmModel realm, RealmAuth auth, EventBuilder event) {
this.realm = realm; this.realm = realm;
this.auth = auth; this.auth = auth;
this.event = event;
auth.init(RealmAuth.Resource.APPLICATION); auth.init(RealmAuth.Resource.APPLICATION);
} }
@ -71,6 +76,9 @@ public class ApplicationsResource {
rep.add(app); rep.add(app);
} }
} }
event.event(EventType.VIEW_APPLICATIONS).representation(rep).success();
return rep; return rep;
} }
@ -88,6 +96,9 @@ public class ApplicationsResource {
try { try {
ApplicationModel applicationModel = RepresentationToModel.createApplication(session, realm, rep, true); ApplicationModel applicationModel = RepresentationToModel.createApplication(session, realm, rep, true);
event.event(EventType.CREATE_APPLICATION).representation(rep).success();
return Response.created(uriInfo.getAbsolutePathBuilder().path(getApplicationPath(applicationModel)).build()).build(); return Response.created(uriInfo.getAbsolutePathBuilder().path(getApplicationPath(applicationModel)).build()).build();
} catch (ModelDuplicateException e) { } catch (ModelDuplicateException e) {
return Flows.errors().exists("Application " + rep.getName() + " already exists"); return Flows.errors().exists("Application " + rep.getName() + " already exists");
@ -110,7 +121,7 @@ public class ApplicationsResource {
if (applicationModel == null) { if (applicationModel == null) {
throw new NotFoundException("Could not find application: " + name); throw new NotFoundException("Could not find application: " + name);
} }
ApplicationResource applicationResource = new ApplicationResource(realm, auth, applicationModel, session); ApplicationResource applicationResource = new ApplicationResource(realm, auth, applicationModel, session, event);
ResteasyProviderFactory.getInstance().injectProperties(applicationResource); ResteasyProviderFactory.getInstance().injectProperties(applicationResource);
//resourceContext.initResource(applicationResource); //resourceContext.initResource(applicationResource);
return applicationResource; return applicationResource;

View file

@ -6,6 +6,8 @@ import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
import org.jboss.resteasy.spi.BadRequestException; import org.jboss.resteasy.spi.BadRequestException;
import org.jboss.resteasy.spi.NotAcceptableException; import org.jboss.resteasy.spi.NotAcceptableException;
import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.NotFoundException;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
@ -21,6 +23,7 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -39,18 +42,20 @@ import java.util.Map;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class ClientAttributeCertificateResource { public class ClientAttributeCertificateResource {
public static final String PRIVATE_KEY = "private.key"; public static final String PRIVATE_KEY = "private.key";
public static final String X509CERTIFICATE = "certificate"; public static final String X509CERTIFICATE = "certificate";
protected RealmModel realm; protected RealmModel realm;
private RealmAuth auth; private RealmAuth auth;
private EventBuilder event;
protected ClientModel client; protected ClientModel client;
protected KeycloakSession session; protected KeycloakSession session;
protected String attributePrefix; protected String attributePrefix;
protected String privateAttribute; protected String privateAttribute;
protected String certificateAttribute; protected String certificateAttribute;
public ClientAttributeCertificateResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session, String attributePrefix) { public ClientAttributeCertificateResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session, String attributePrefix, EventBuilder event) {
this.realm = realm; this.realm = realm;
this.auth = auth; this.auth = auth;
this.client = client; this.client = client;
@ -58,6 +63,7 @@ public class ClientAttributeCertificateResource {
this.attributePrefix = attributePrefix; this.attributePrefix = attributePrefix;
this.privateAttribute = attributePrefix + "." + PRIVATE_KEY; this.privateAttribute = attributePrefix + "." + PRIVATE_KEY;
this.certificateAttribute = attributePrefix + "." + X509CERTIFICATE; this.certificateAttribute = attributePrefix + "." + X509CERTIFICATE;
this.event = event;
} }
public static class ClientKeyPairInfo { public static class ClientKeyPairInfo {
@ -93,6 +99,9 @@ public class ClientAttributeCertificateResource {
ClientKeyPairInfo info = new ClientKeyPairInfo(); ClientKeyPairInfo info = new ClientKeyPairInfo();
info.setCertificate(client.getAttribute(certificateAttribute)); info.setCertificate(client.getAttribute(certificateAttribute));
info.setPrivateKey(client.getAttribute(privateAttribute)); info.setPrivateKey(client.getAttribute(privateAttribute));
event.event(EventType.VIEW_CLIENT_CERTIFICATE).representation(info).success();
return info; return info;
} }
@ -131,6 +140,9 @@ public class ClientAttributeCertificateResource {
ClientKeyPairInfo info = new ClientKeyPairInfo(); ClientKeyPairInfo info = new ClientKeyPairInfo();
info.setCertificate(client.getAttribute(certificateAttribute)); info.setCertificate(client.getAttribute(certificateAttribute));
info.setPrivateKey(client.getAttribute(privateAttribute)); info.setPrivateKey(client.getAttribute(privateAttribute));
event.event(EventType.UPDATE_CLIENT_CERTIFICATE).representation(info).success();
return info; return info;
} }
@ -188,6 +200,7 @@ public class ClientAttributeCertificateResource {
info.setCertificate(certPem); info.setCertificate(certPem);
} }
event.event(EventType.UPDATE_CLIENT_CERTIFICATE).representation(info).success();
return info; return info;
} }
@ -314,6 +327,9 @@ public class ClientAttributeCertificateResource {
stream.flush(); stream.flush();
stream.close(); stream.close();
byte[] rtn = stream.toByteArray(); byte[] rtn = stream.toByteArray();
event.event(EventType.VIEW_CLIENT_CERTIFICATE).representation(rtn).success();
return rtn; return rtn;
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);

View file

@ -4,6 +4,8 @@ import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.broker.provider.IdentityProvider; import org.keycloak.broker.provider.IdentityProvider;
import org.keycloak.broker.provider.IdentityProviderFactory; import org.keycloak.broker.provider.IdentityProviderFactory;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.ClientIdentityProviderMappingModel; import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.FederatedIdentityModel; import org.keycloak.models.FederatedIdentityModel;
@ -29,6 +31,7 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -43,28 +46,45 @@ public class IdentityProviderResource {
private final RealmModel realm; private final RealmModel realm;
private final KeycloakSession session; private final KeycloakSession session;
private final IdentityProviderModel identityProviderModel; private final IdentityProviderModel identityProviderModel;
private EventBuilder event;
public IdentityProviderResource(RealmAuth auth, RealmModel realm, KeycloakSession session, IdentityProviderModel identityProviderModel) { public IdentityProviderResource(RealmAuth auth, RealmModel realm, KeycloakSession session, IdentityProviderModel identityProviderModel, EventBuilder event) {
this.realm = realm; this.realm = realm;
this.session = session; this.session = session;
this.identityProviderModel = identityProviderModel; this.identityProviderModel = identityProviderModel;
this.auth = auth; this.auth = auth;
this.event = event;
} }
@GET @GET
@NoCache @NoCache
@Produces("application/json") @Produces("application/json")
public IdentityProviderRepresentation getIdentityProvider() { public IdentityProviderRepresentation getIdentityProvider() {
return ModelToRepresentation.toRepresentation(this.identityProviderModel); IdentityProviderRepresentation rep = ModelToRepresentation.toRepresentation(this.identityProviderModel);
event.event(EventType.VIEW_IDENTITY_PROVIDER)
.representation(rep)
.success();
return rep;
} }
@DELETE @DELETE
@NoCache @NoCache
public Response delete() { public Response delete() {
this.auth.requireManage(); this.auth.requireManage();
IdentityProviderRepresentation rep = getIdentityProvider();
removeClientIdentityProviders(this.realm.getApplications(), this.identityProviderModel); removeClientIdentityProviders(this.realm.getApplications(), this.identityProviderModel);
removeClientIdentityProviders(this.realm.getOAuthClients(), this.identityProviderModel); removeClientIdentityProviders(this.realm.getOAuthClients(), this.identityProviderModel);
this.realm.removeIdentityProviderByAlias(this.identityProviderModel.getAlias()); this.realm.removeIdentityProviderByAlias(this.identityProviderModel.getAlias());
event.event(EventType.DELETE_IDENTITY_PROVIDER)
.representation(rep)
.success();
return Response.noContent().build(); return Response.noContent().build();
} }
@ -90,6 +110,10 @@ public class IdentityProviderResource {
updateUsersAfterProviderAliasChange(this.session.users().getUsers(this.realm), oldProviderId, newProviderId); updateUsersAfterProviderAliasChange(this.session.users().getUsers(this.realm), oldProviderId, newProviderId);
} }
event.event(EventType.UPDATE_IDENTITY_PROVIDER)
.representation(providerRep)
.success();
return Response.noContent().build(); return Response.noContent().build();
} catch (ModelDuplicateException e) { } catch (ModelDuplicateException e) {
return Flows.errors().exists("Identity Provider " + providerRep.getAlias() + " already exists"); return Flows.errors().exists("Identity Provider " + providerRep.getAlias() + " already exists");

View file

@ -8,8 +8,8 @@ import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.broker.provider.IdentityProvider; import org.keycloak.broker.provider.IdentityProvider;
import org.keycloak.broker.provider.IdentityProviderFactory; import org.keycloak.broker.provider.IdentityProviderFactory;
import org.keycloak.models.ClientIdentityProviderMappingModel; import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientModel; import org.keycloak.events.EventType;
import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
@ -32,6 +32,7 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
@ -48,11 +49,13 @@ public class IdentityProvidersResource {
private final RealmModel realm; private final RealmModel realm;
private final KeycloakSession session; private final KeycloakSession session;
private RealmAuth auth; private RealmAuth auth;
private EventBuilder event;
public IdentityProvidersResource(RealmModel realm, KeycloakSession session, RealmAuth auth) { public IdentityProvidersResource(RealmModel realm, KeycloakSession session, RealmAuth auth, EventBuilder event) {
this.realm = realm; this.realm = realm;
this.session = session; this.session = session;
this.auth = auth; this.auth = auth;
this.event = event;
this.auth.init(RealmAuth.Resource.IDENTITY_PROVIDER); this.auth.init(RealmAuth.Resource.IDENTITY_PROVIDER);
} }
@ -65,6 +68,10 @@ public class IdentityProvidersResource {
IdentityProviderFactory providerFactory = getProviderFactorytById(providerId); IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
if (providerFactory != null) { if (providerFactory != null) {
event.event(EventType.VIEW_IDENTITY_PROVIDERS)
.representation(providerFactory)
.success();
return Response.ok(providerFactory).build(); return Response.ok(providerFactory).build();
} }
@ -83,6 +90,11 @@ public class IdentityProvidersResource {
InputStream inputStream = file.getBody(InputStream.class, null); InputStream inputStream = file.getBody(InputStream.class, null);
IdentityProviderFactory providerFactory = getProviderFactorytById(providerId); IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
Map<String, String> config = providerFactory.parseConfig(inputStream); Map<String, String> config = providerFactory.parseConfig(inputStream);
event.event(EventType.CREATE_IDENTITY_PROVIDER)
.representation(config)
.success();
return config; return config;
} }
@ -104,6 +116,11 @@ public class IdentityProvidersResource {
} }
IdentityProviderFactory providerFactory = getProviderFactorytById(providerId); IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
Map<String, String> config = providerFactory.parseConfig(inputStream); Map<String, String> config = providerFactory.parseConfig(inputStream);
event.event(EventType.CREATE_IDENTITY_PROVIDER)
.representation(config)
.success();
return config; return config;
} }
@ -120,6 +137,8 @@ public class IdentityProvidersResource {
representations.add(ModelToRepresentation.toRepresentation(identityProviderModel)); representations.add(ModelToRepresentation.toRepresentation(identityProviderModel));
} }
event.event(EventType.VIEW_IDENTITY_PROVIDERS).representation(representations).success();
return representations; return representations;
} }
@ -132,6 +151,10 @@ public class IdentityProvidersResource {
try { try {
this.realm.addIdentityProvider(RepresentationToModel.toModel(representation)); this.realm.addIdentityProvider(RepresentationToModel.toModel(representation));
event.event(EventType.CREATE_IDENTITY_PROVIDER)
.representation(representation)
.success();
return Response.created(uriInfo.getAbsolutePathBuilder().path(representation.getProviderId()).build()).build(); return Response.created(uriInfo.getAbsolutePathBuilder().path(representation.getProviderId()).build()).build();
} catch (ModelDuplicateException e) { } catch (ModelDuplicateException e) {
return Flows.errors().exists("Identity Provider " + representation.getAlias() + " already exists"); return Flows.errors().exists("Identity Provider " + representation.getAlias() + " already exists");
@ -154,9 +177,13 @@ public class IdentityProvidersResource {
throw new NotFoundException("Could not find identity provider: " + alias); throw new NotFoundException("Could not find identity provider: " + alias);
} }
IdentityProviderResource identityProviderResource = new IdentityProviderResource(this.auth, realm, session, identityProviderModel); IdentityProviderResource identityProviderResource = new IdentityProviderResource(this.auth, realm, session, identityProviderModel, event);
ResteasyProviderFactory.getInstance().injectProperties(identityProviderResource); ResteasyProviderFactory.getInstance().injectProperties(identityProviderResource);
event.event(EventType.VIEW_IDENTITY_PROVIDER)
.representation(identityProviderResource)
.success();
return identityProviderResource; return identityProviderResource;
} }

View file

@ -4,6 +4,8 @@ import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.OAuthClientModel; import org.keycloak.models.OAuthClientModel;
@ -31,6 +33,7 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.io.IOException; import java.io.IOException;
/** /**
@ -43,6 +46,7 @@ public class OAuthClientResource {
protected static final Logger logger = Logger.getLogger(RealmAdminResource.class); protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
protected RealmModel realm; protected RealmModel realm;
private RealmAuth auth; private RealmAuth auth;
private EventBuilder event;
protected OAuthClientModel oauthClient; protected OAuthClientModel oauthClient;
protected KeycloakSession session; protected KeycloakSession session;
@Context @Context
@ -55,11 +59,12 @@ public class OAuthClientResource {
return (KeycloakApplication)application; return (KeycloakApplication)application;
} }
public OAuthClientResource(RealmModel realm, RealmAuth auth, OAuthClientModel oauthClient, KeycloakSession session) { public OAuthClientResource(RealmModel realm, RealmAuth auth, OAuthClientModel oauthClient, KeycloakSession session, EventBuilder event) {
this.realm = realm; this.realm = realm;
this.auth = auth; this.auth = auth;
this.oauthClient = oauthClient; this.oauthClient = oauthClient;
this.session = session; this.session = session;
this.event = event;
auth.init(RealmAuth.Resource.CLIENT); auth.init(RealmAuth.Resource.CLIENT);
} }
@ -84,7 +89,7 @@ public class OAuthClientResource {
*/ */
@Path("certificates/{attr}") @Path("certificates/{attr}")
public ClientAttributeCertificateResource getCertficateResource(@PathParam("attr") String attributePrefix) { public ClientAttributeCertificateResource getCertficateResource(@PathParam("attr") String attributePrefix) {
return new ClientAttributeCertificateResource(realm, auth, oauthClient, session, attributePrefix); return new ClientAttributeCertificateResource(realm, auth, oauthClient, session, attributePrefix, event);
} }
@ -102,6 +107,8 @@ public class OAuthClientResource {
try { try {
RepresentationToModel.updateOAuthClient(session, rep, oauthClient); RepresentationToModel.updateOAuthClient(session, rep, oauthClient);
event.event(EventType.UPDATE_OAUTH_CLIENT).representation(rep).success();
return Response.noContent().build(); return Response.noContent().build();
} catch (ModelDuplicateException e) { } catch (ModelDuplicateException e) {
return Flows.errors().exists("Client " + rep.getName() + " already exists"); return Flows.errors().exists("Client " + rep.getName() + " already exists");
@ -119,7 +126,11 @@ public class OAuthClientResource {
public OAuthClientRepresentation getOAuthClient() { public OAuthClientRepresentation getOAuthClient() {
auth.requireView(); auth.requireView();
return ModelToRepresentation.toRepresentation(oauthClient); OAuthClientRepresentation rep = ModelToRepresentation.toRepresentation(oauthClient);
event.event(EventType.VIEW_OAUTH_CLIENT).representation(rep).success();
return rep;
} }
/** /**
@ -151,6 +162,10 @@ public class OAuthClientResource {
public void deleteOAuthClient() { public void deleteOAuthClient() {
auth.requireManage(); auth.requireManage();
OAuthClientRepresentation rep = getOAuthClient();
event.event(EventType.DELETE_OAUTH_CLIENT).representation(rep).success();
new OAuthClientManager(new RealmManager(session)).removeClient(realm, oauthClient); new OAuthClientManager(new RealmManager(session)).removeClient(realm, oauthClient);
} }

View file

@ -1,38 +1,17 @@
package org.keycloak.services.resources.admin; package org.keycloak.services.resources.admin;
import org.jboss.logging.Logger; import org.keycloak.events.EventBuilder;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.OAuthClientModel; import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.OAuthClientRepresentation;
import org.keycloak.services.resources.flows.Flows;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
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.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.ArrayList;
import java.util.List;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class OAuthClientsByIdResource extends OAuthClientsResource { public class OAuthClientsByIdResource extends OAuthClientsResource {
public OAuthClientsByIdResource(RealmModel realm, RealmAuth auth, KeycloakSession session) { public OAuthClientsByIdResource(RealmModel realm, RealmAuth auth, KeycloakSession session, EventBuilder event) {
super(realm, auth, session); super(realm, auth, session, event);
} }
@Override @Override

View file

@ -4,6 +4,8 @@ import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.OAuthClientModel; import org.keycloak.models.OAuthClientModel;
@ -23,6 +25,7 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -33,7 +36,7 @@ import java.util.List;
public class OAuthClientsResource { public class OAuthClientsResource {
protected static final Logger logger = Logger.getLogger(RealmAdminResource.class); protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
protected RealmModel realm; protected RealmModel realm;
private EventBuilder event;
protected KeycloakSession session; protected KeycloakSession session;
/* /*
@ -43,10 +46,11 @@ public class OAuthClientsResource {
*/ */
private RealmAuth auth; private RealmAuth auth;
public OAuthClientsResource(RealmModel realm, RealmAuth auth, KeycloakSession session) { public OAuthClientsResource(RealmModel realm, RealmAuth auth, KeycloakSession session, EventBuilder event) {
this.auth = auth; this.auth = auth;
this.realm = realm; this.realm = realm;
this.session = session; this.session = session;
this.event = event;
auth.init(RealmAuth.Resource.CLIENT); auth.init(RealmAuth.Resource.CLIENT);
} }
@ -73,6 +77,9 @@ public class OAuthClientsResource {
rep.add(client); rep.add(client);
} }
} }
event.event(EventType.VIEW_OAUTH_CLIENTS).representation(rep).success();
return rep; return rep;
} }
@ -90,6 +97,8 @@ public class OAuthClientsResource {
try { try {
OAuthClientModel oauth = RepresentationToModel.createOAuthClient(session, rep, realm); OAuthClientModel oauth = RepresentationToModel.createOAuthClient(session, rep, realm);
event.event(EventType.CREATE_OAUTH_CLIENT).representation(rep).success();
return Response.created(uriInfo.getAbsolutePathBuilder().path(getClientPath(oauth)).build()).build(); return Response.created(uriInfo.getAbsolutePathBuilder().path(getClientPath(oauth)).build()).build();
} catch (ModelDuplicateException e) { } catch (ModelDuplicateException e) {
return Flows.errors().exists("Client " + rep.getName() + " already exists"); return Flows.errors().exists("Client " + rep.getName() + " already exists");
@ -114,7 +123,7 @@ public class OAuthClientsResource {
if (oauth == null) { if (oauth == null) {
throw new NotFoundException("OAuth Client not found"); throw new NotFoundException("OAuth Client not found");
} }
OAuthClientResource oAuthClientResource = new OAuthClientResource(realm, auth, oauth, session); OAuthClientResource oAuthClientResource = new OAuthClientResource(realm, auth, oauth, session, event);
ResteasyProviderFactory.getInstance().injectProperties(oAuthClientResource); ResteasyProviderFactory.getInstance().injectProperties(oAuthClientResource);
//resourceContext.initResource(oAuthClientResource); //resourceContext.initResource(oAuthClientResource);
return oAuthClientResource; return oAuthClientResource;

View file

@ -26,6 +26,7 @@ import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.ClientConnection; import org.keycloak.ClientConnection;
import org.keycloak.events.Event; import org.keycloak.events.Event;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventQuery; import org.keycloak.events.EventQuery;
import org.keycloak.events.EventStoreProvider; import org.keycloak.events.EventStoreProvider;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
@ -63,6 +64,7 @@ public class RealmAdminResource {
protected RealmAuth auth; protected RealmAuth auth;
protected RealmModel realm; protected RealmModel realm;
private TokenManager tokenManager; private TokenManager tokenManager;
private EventBuilder event;
@Context @Context
protected KeycloakSession session; protected KeycloakSession session;
@ -76,10 +78,11 @@ public class RealmAdminResource {
@Context @Context
protected HttpHeaders headers; protected HttpHeaders headers;
public RealmAdminResource(RealmAuth auth, RealmModel realm, TokenManager tokenManager) { public RealmAdminResource(RealmAuth auth, RealmModel realm, TokenManager tokenManager, EventBuilder event) {
this.auth = auth; this.auth = auth;
this.realm = realm; this.realm = realm;
this.tokenManager = tokenManager; this.tokenManager = tokenManager;
this.event = event;
auth.init(RealmAuth.Resource.REALM); auth.init(RealmAuth.Resource.REALM);
} }
@ -102,7 +105,7 @@ public class RealmAdminResource {
*/ */
@Path("applications") @Path("applications")
public ApplicationsResource getApplications() { public ApplicationsResource getApplications() {
ApplicationsResource applicationsResource = new ApplicationsResource(realm, auth); ApplicationsResource applicationsResource = new ApplicationsResource(realm, auth, event);
ResteasyProviderFactory.getInstance().injectProperties(applicationsResource); ResteasyProviderFactory.getInstance().injectProperties(applicationsResource);
//resourceContext.initResource(applicationsResource); //resourceContext.initResource(applicationsResource);
return applicationsResource; return applicationsResource;
@ -115,7 +118,7 @@ public class RealmAdminResource {
*/ */
@Path("applications-by-id") @Path("applications-by-id")
public ApplicationsByIdResource getApplicationsById() { public ApplicationsByIdResource getApplicationsById() {
ApplicationsByIdResource applicationsResource = new ApplicationsByIdResource(realm, auth); ApplicationsByIdResource applicationsResource = new ApplicationsByIdResource(realm, auth, event);
ResteasyProviderFactory.getInstance().injectProperties(applicationsResource); ResteasyProviderFactory.getInstance().injectProperties(applicationsResource);
//resourceContext.initResource(applicationsResource); //resourceContext.initResource(applicationsResource);
return applicationsResource; return applicationsResource;
@ -128,7 +131,7 @@ public class RealmAdminResource {
*/ */
@Path("oauth-clients") @Path("oauth-clients")
public OAuthClientsResource getOAuthClients() { public OAuthClientsResource getOAuthClients() {
OAuthClientsResource oauth = new OAuthClientsResource(realm, auth, session); OAuthClientsResource oauth = new OAuthClientsResource(realm, auth, session, event);
ResteasyProviderFactory.getInstance().injectProperties(oauth); ResteasyProviderFactory.getInstance().injectProperties(oauth);
//resourceContext.initResource(oauth); //resourceContext.initResource(oauth);
return oauth; return oauth;
@ -141,7 +144,7 @@ public class RealmAdminResource {
*/ */
@Path("oauth-clients-by-id") @Path("oauth-clients-by-id")
public OAuthClientsByIdResource getOAuthClientsById() { public OAuthClientsByIdResource getOAuthClientsById() {
OAuthClientsByIdResource oauth = new OAuthClientsByIdResource(realm, auth, session); OAuthClientsByIdResource oauth = new OAuthClientsByIdResource(realm, auth, session, event);
ResteasyProviderFactory.getInstance().injectProperties(oauth); ResteasyProviderFactory.getInstance().injectProperties(oauth);
//resourceContext.initResource(oauth); //resourceContext.initResource(oauth);
return oauth; return oauth;
@ -154,7 +157,7 @@ public class RealmAdminResource {
*/ */
@Path("roles") @Path("roles")
public RoleContainerResource getRoleContainerResource() { public RoleContainerResource getRoleContainerResource() {
return new RoleContainerResource(realm, auth, realm); return new RoleContainerResource(realm, auth, realm, event);
} }
/** /**
@ -177,6 +180,9 @@ public class RealmAdminResource {
CacheUserProvider cache = (CacheUserProvider)session.userStorage(); CacheUserProvider cache = (CacheUserProvider)session.userStorage();
rep.setUserCacheEnabled(cache.isEnabled()); rep.setUserCacheEnabled(cache.isEnabled());
} }
event.event(EventType.VIEW_REALM).representation(rep).success();
return rep; return rep;
} else { } else {
auth.requireAny(); auth.requireAny();
@ -184,6 +190,8 @@ public class RealmAdminResource {
RealmRepresentation rep = new RealmRepresentation(); RealmRepresentation rep = new RealmRepresentation();
rep.setRealm(realm.getName()); rep.setRealm(realm.getName());
event.event(EventType.VIEW_REALM).representation(rep).success();
return rep; return rep;
} }
} }
@ -219,6 +227,8 @@ public class RealmAdminResource {
usersSyncManager.refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), fedProvider, realm.getId()); usersSyncManager.refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), fedProvider, realm.getId());
} }
event.event(EventType.UPDATE_REALM).representation(rep).success();
return Response.noContent().build(); return Response.noContent().build();
} catch (ModelDuplicateException e) { } catch (ModelDuplicateException e) {
return Flows.errors().exists("Realm " + rep.getRealm() + " already exists"); return Flows.errors().exists("Realm " + rep.getRealm() + " already exists");
@ -233,9 +243,13 @@ public class RealmAdminResource {
public void deleteRealm() { public void deleteRealm() {
auth.requireManage(); auth.requireManage();
RealmRepresentation rep = getRealm();
if (!new RealmManager(session).removeRealm(realm)) { if (!new RealmManager(session).removeRealm(realm)) {
throw new NotFoundException("Realm doesn't exist"); throw new NotFoundException("Realm doesn't exist");
} }
event.event(EventType.DELETE_REALM).representation(rep).success();
} }
/** /**
@ -245,7 +259,7 @@ public class RealmAdminResource {
*/ */
@Path("users") @Path("users")
public UsersResource users() { public UsersResource users() {
UsersResource users = new UsersResource(realm, auth, tokenManager); UsersResource users = new UsersResource(realm, auth, tokenManager, event);
ResteasyProviderFactory.getInstance().injectProperties(users); ResteasyProviderFactory.getInstance().injectProperties(users);
//resourceContext.initResource(users); //resourceContext.initResource(users);
return users; return users;
@ -253,7 +267,7 @@ public class RealmAdminResource {
@Path("user-federation") @Path("user-federation")
public UserFederationResource userFederation() { public UserFederationResource userFederation() {
UserFederationResource fed = new UserFederationResource(realm, auth); UserFederationResource fed = new UserFederationResource(realm, auth, event);
ResteasyProviderFactory.getInstance().injectProperties(fed); ResteasyProviderFactory.getInstance().injectProperties(fed);
//resourceContext.initResource(fed); //resourceContext.initResource(fed);
return fed; return fed;
@ -266,7 +280,7 @@ public class RealmAdminResource {
*/ */
@Path("roles-by-id") @Path("roles-by-id")
public RoleByIdResource rolesById() { public RoleByIdResource rolesById() {
RoleByIdResource resource = new RoleByIdResource(realm, auth); RoleByIdResource resource = new RoleByIdResource(realm, auth, event);
ResteasyProviderFactory.getInstance().injectProperties(resource); ResteasyProviderFactory.getInstance().injectProperties(resource);
//resourceContext.initResource(resource); //resourceContext.initResource(resource);
return resource; return resource;
@ -473,6 +487,6 @@ public class RealmAdminResource {
@Path("identity-provider") @Path("identity-provider")
public IdentityProvidersResource getIdentityProviderResource() { public IdentityProvidersResource getIdentityProviderResource() {
return new IdentityProvidersResource(realm, session, this.auth); return new IdentityProvidersResource(realm, session, this.auth, event);
} }
} }

View file

@ -6,6 +6,9 @@ import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.events.Details;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.AdminRoles; import org.keycloak.models.AdminRoles;
import org.keycloak.models.ApplicationModel; import org.keycloak.models.ApplicationModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
@ -33,6 +36,7 @@ import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
@ -49,10 +53,12 @@ public class RealmsAdminResource {
protected static final Logger logger = Logger.getLogger(RealmsAdminResource.class); protected static final Logger logger = Logger.getLogger(RealmsAdminResource.class);
protected AdminAuth auth; protected AdminAuth auth;
protected TokenManager tokenManager; protected TokenManager tokenManager;
private EventBuilder event;
public RealmsAdminResource(AdminAuth auth, TokenManager tokenManager) { public RealmsAdminResource(AdminAuth auth, TokenManager tokenManager, EventBuilder event) {
this.auth = auth; this.auth = auth;
this.tokenManager = tokenManager; this.tokenManager = tokenManager;
this.event = event;
} }
public static final CacheControl noCache = new CacheControl(); public static final CacheControl noCache = new CacheControl();
@ -72,7 +78,6 @@ public class RealmsAdminResource {
@Context @Context
protected KeycloakApplication keycloak; protected KeycloakApplication keycloak;
/** /**
* Returns a list of realms. This list is filtered based on what realms the caller is allowed to view. * Returns a list of realms. This list is filtered based on what realms the caller is allowed to view.
* *
@ -135,6 +140,11 @@ public class RealmsAdminResource {
URI location = AdminRoot.realmsUrl(uriInfo).path(realm.getName()).build(); URI location = AdminRoot.realmsUrl(uriInfo).path(realm.getName()).build();
logger.debugv("imported realm success, sending back: {0}", location.toString()); logger.debugv("imported realm success, sending back: {0}", location.toString());
event.event(EventType.CREATE_REALM)
.representation(rep)
.success();
return Response.created(location).build(); return Response.created(location).build();
} catch (ModelDuplicateException e) { } catch (ModelDuplicateException e) {
return Flows.errors().exists("Realm " + rep.getRealm() + " already exists"); return Flows.errors().exists("Realm " + rep.getRealm() + " already exists");
@ -181,6 +191,8 @@ public class RealmsAdminResource {
URI location = AdminRoot.realmsUrl(uriInfo).path(realm.getName()).build(); URI location = AdminRoot.realmsUrl(uriInfo).path(realm.getName()).build();
return Response.created(location).build(); return Response.created(location).build();
} }
event.event(EventType.CREATE_REALM).representation(rep).success();
} }
return Response.noContent().build(); return Response.noContent().build();
@ -225,7 +237,8 @@ public class RealmsAdminResource {
realmAuth = new RealmAuth(auth, realm.getApplicationByName(realmManager.getRealmAdminApplicationName(auth.getRealm()))); realmAuth = new RealmAuth(auth, realm.getApplicationByName(realmManager.getRealmAdminApplicationName(auth.getRealm())));
} }
RealmAdminResource adminResource = new RealmAdminResource(realmAuth, realm, tokenManager); event.detail(Details.REALM, realm.getName());
RealmAdminResource adminResource = new RealmAdminResource(realmAuth, realm, tokenManager, event);
ResteasyProviderFactory.getInstance().injectProperties(adminResource); ResteasyProviderFactory.getInstance().injectProperties(adminResource);
//resourceContext.initResource(adminResource); //resourceContext.initResource(adminResource);
return adminResource; return adminResource;

View file

@ -3,7 +3,10 @@ package org.keycloak.services.resources.admin;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.NotFoundException;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.ApplicationModel; import org.keycloak.models.ApplicationModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OAuthClientModel; import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
@ -18,6 +21,8 @@ import javax.ws.rs.PUT;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -31,12 +36,17 @@ public class RoleByIdResource extends RoleResource {
protected static final Logger logger = Logger.getLogger(RoleByIdResource.class); protected static final Logger logger = Logger.getLogger(RoleByIdResource.class);
private final RealmModel realm; private final RealmModel realm;
private final RealmAuth auth; private final RealmAuth auth;
private EventBuilder event;
public RoleByIdResource(RealmModel realm, RealmAuth auth) { @Context
protected KeycloakSession session;
public RoleByIdResource(RealmModel realm, RealmAuth auth, EventBuilder event) {
super(realm); super(realm);
this.realm = realm; this.realm = realm;
this.auth = auth; this.auth = auth;
this.event = event;
} }
/** /**
@ -52,7 +62,14 @@ public class RoleByIdResource extends RoleResource {
public RoleRepresentation getRole(final @PathParam("role-id") String id) { public RoleRepresentation getRole(final @PathParam("role-id") String id) {
RoleModel roleModel = getRoleModel(id); RoleModel roleModel = getRoleModel(id);
auth.requireView(); auth.requireView();
return getRole(roleModel);
RoleRepresentation rep = getRole(roleModel);
event.event(EventType.VIEW_ROLE)
.representation(rep)
.success();
return rep;
} }
protected RoleModel getRoleModel(String id) { protected RoleModel getRoleModel(String id) {
@ -85,9 +102,14 @@ public class RoleByIdResource extends RoleResource {
@DELETE @DELETE
@NoCache @NoCache
public void deleteRole(final @PathParam("role-id") String id) { public void deleteRole(final @PathParam("role-id") String id) {
RoleRepresentation rep = getRole(id);
RoleModel role = getRoleModel(id); RoleModel role = getRoleModel(id);
auth.requireManage(); auth.requireManage();
deleteRole(role); deleteRole(role);
event.event(EventType.DELETE_ROLE)
.representation(rep)
.success();
} }
/** /**
@ -103,6 +125,10 @@ public class RoleByIdResource extends RoleResource {
RoleModel role = getRoleModel(id); RoleModel role = getRoleModel(id);
auth.requireManage(); auth.requireManage();
updateRole(rep, role); updateRole(rep, role);
event.event(EventType.UPDATE_ROLE)
.representation(rep)
.success();
} }
/** /**
@ -118,6 +144,12 @@ public class RoleByIdResource extends RoleResource {
RoleModel role = getRoleModel(id); RoleModel role = getRoleModel(id);
auth.requireManage(); auth.requireManage();
addComposites(roles, role); addComposites(roles, role);
RoleRepresentation rep = getRole(id);
event.event(EventType.UPDATE_ROLE)
.representation(rep)
.success();
} }
/** /**

View file

@ -2,6 +2,8 @@ package org.keycloak.services.resources.admin;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.NotFoundException;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.ApplicationModel; import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
@ -22,6 +24,7 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -34,12 +37,14 @@ public class RoleContainerResource extends RoleResource {
private final RealmModel realm; private final RealmModel realm;
private final RealmAuth auth; private final RealmAuth auth;
protected RoleContainerModel roleContainer; protected RoleContainerModel roleContainer;
private EventBuilder event;
public RoleContainerResource(RealmModel realm, RealmAuth auth, RoleContainerModel roleContainer) { public RoleContainerResource(RealmModel realm, RealmAuth auth, RoleContainerModel roleContainer, EventBuilder event) {
super(realm); super(realm);
this.realm = realm; this.realm = realm;
this.auth = auth; this.auth = auth;
this.roleContainer = roleContainer; this.roleContainer = roleContainer;
this.event = event;
} }
/** /**
@ -58,6 +63,9 @@ public class RoleContainerResource extends RoleResource {
for (RoleModel roleModel : roleModels) { for (RoleModel roleModel : roleModels) {
roles.add(ModelToRepresentation.toRepresentation(roleModel)); roles.add(ModelToRepresentation.toRepresentation(roleModel));
} }
event.event(EventType.VIEW_ROLES).representation(roles).success();
return roles; return roles;
} }
@ -76,6 +84,11 @@ public class RoleContainerResource extends RoleResource {
try { try {
RoleModel role = roleContainer.addRole(rep.getName()); RoleModel role = roleContainer.addRole(rep.getName());
role.setDescription(rep.getDescription()); role.setDescription(rep.getDescription());
event.event(EventType.CREATE_ROLE)
.representation(rep)
.success();
return Response.created(uriInfo.getAbsolutePathBuilder().path(role.getName()).build()).build(); return Response.created(uriInfo.getAbsolutePathBuilder().path(role.getName()).build()).build();
} catch (ModelDuplicateException e) { } catch (ModelDuplicateException e) {
return Flows.errors().exists("Role with name " + rep.getName() + " already exists"); return Flows.errors().exists("Role with name " + rep.getName() + " already exists");
@ -99,7 +112,14 @@ public class RoleContainerResource extends RoleResource {
if (roleModel == null) { if (roleModel == null) {
throw new NotFoundException("Could not find role: " + roleName); throw new NotFoundException("Could not find role: " + roleName);
} }
return getRole(roleModel);
RoleRepresentation rep = getRole(roleModel);
event.event(EventType.VIEW_ROLE)
.representation(rep)
.success();
return rep;
} }
/** /**
@ -113,11 +133,16 @@ public class RoleContainerResource extends RoleResource {
public void deleteRole(final @PathParam("role-name") String roleName) { public void deleteRole(final @PathParam("role-name") String roleName) {
auth.requireManage(); auth.requireManage();
RoleRepresentation rep = getRole(roleName);
RoleModel role = roleContainer.getRole(roleName); RoleModel role = roleContainer.getRole(roleName);
if (role == null) { if (role == null) {
throw new NotFoundException("Could not find role: " + roleName); throw new NotFoundException("Could not find role: " + roleName);
} }
deleteRole(role); deleteRole(role);
event.event(EventType.DELETE_ROLE)
.representation(rep)
.success();
} }
/** /**
@ -139,6 +164,11 @@ public class RoleContainerResource extends RoleResource {
} }
try { try {
updateRole(rep, role); updateRole(rep, role);
event.event(EventType.UPDATE_ROLE)
.representation(rep)
.success();
return Response.noContent().build(); return Response.noContent().build();
} catch (ModelDuplicateException e) { } catch (ModelDuplicateException e) {
return Flows.errors().exists("Role with name " + rep.getName() + " already exists"); return Flows.errors().exists("Role with name " + rep.getName() + " already exists");

View file

@ -4,6 +4,7 @@ import org.keycloak.Version;
import org.keycloak.broker.provider.IdentityProvider; import org.keycloak.broker.provider.IdentityProvider;
import org.keycloak.broker.provider.IdentityProviderFactory; import org.keycloak.broker.provider.IdentityProviderFactory;
import org.keycloak.events.EventListenerProvider; import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.EventType;
import org.keycloak.exportimport.ApplicationImporter; import org.keycloak.exportimport.ApplicationImporter;
import org.keycloak.exportimport.ApplicationImporterFactory; import org.keycloak.exportimport.ApplicationImporterFactory;
import org.keycloak.freemarker.Theme; import org.keycloak.freemarker.Theme;
@ -16,7 +17,6 @@ import org.keycloak.protocol.LoginProtocolFactory;
import org.keycloak.protocol.ProtocolMapper; import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi; import org.keycloak.provider.Spi;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.ProtocolMapperTypeRepresentation; import org.keycloak.representations.idm.ProtocolMapperTypeRepresentation;
import org.keycloak.social.SocialIdentityProvider; import org.keycloak.social.SocialIdentityProvider;
@ -24,7 +24,6 @@ import org.keycloak.social.SocialIdentityProvider;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
@ -60,6 +59,7 @@ public class ServerInfoAdminResource {
setProviders(info); setProviders(info);
setProtocolMapperTypes(info); setProtocolMapperTypes(info);
setBuiltinProtocolMappers(info); setBuiltinProtocolMappers(info);
setEventTypes(info);
return info; return info;
} }
@ -178,6 +178,15 @@ public class ServerInfoAdminResource {
} }
} }
private void setEventTypes(ServerInfoRepresentation info) {
List<String> eventTypes = new LinkedList<>();
for (EventType t : EventType.values()) {
eventTypes.add(t.name());
}
Collections.sort(eventTypes);
info.setEventTypes(eventTypes);
}
public static class ServerInfoRepresentation { public static class ServerInfoRepresentation {
private String version; private String version;
@ -197,6 +206,8 @@ public class ServerInfoAdminResource {
private Map<String, List<ProtocolMapperTypeRepresentation>> protocolMapperTypes; private Map<String, List<ProtocolMapperTypeRepresentation>> protocolMapperTypes;
private Map<String, List<ProtocolMapperRepresentation>> builtinProtocolMappers; private Map<String, List<ProtocolMapperRepresentation>> builtinProtocolMappers;
private List<String> eventTypes;
public ServerInfoRepresentation() { public ServerInfoRepresentation() {
} }
@ -247,6 +258,14 @@ public class ServerInfoAdminResource {
public void setBuiltinProtocolMappers(Map<String, List<ProtocolMapperRepresentation>> builtinProtocolMappers) { public void setBuiltinProtocolMappers(Map<String, List<ProtocolMapperRepresentation>> builtinProtocolMappers) {
this.builtinProtocolMappers = builtinProtocolMappers; this.builtinProtocolMappers = builtinProtocolMappers;
} }
public List<String> getEventTypes() {
return eventTypes;
}
public void setEventTypes(List<String> eventTypes) {
this.eventTypes = eventTypes;
}
} }
} }

View file

@ -11,6 +11,8 @@ import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderFactory; import org.keycloak.models.UserFederationProviderFactory;
import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.constants.KerberosConstants; import org.keycloak.constants.KerberosConstants;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.ProviderFactory;
import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation; import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation;
@ -30,6 +32,7 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -46,15 +49,18 @@ public class UserFederationResource {
protected RealmAuth auth; protected RealmAuth auth;
private EventBuilder event;
@Context @Context
protected UriInfo uriInfo; protected UriInfo uriInfo;
@Context @Context
protected KeycloakSession session; protected KeycloakSession session;
public UserFederationResource(RealmModel realm, RealmAuth auth) { public UserFederationResource(RealmModel realm, RealmAuth auth, EventBuilder event) {
this.auth = auth; this.auth = auth;
this.realm = realm; this.realm = realm;
this.event = event;
auth.init(RealmAuth.Resource.USER); auth.init(RealmAuth.Resource.USER);
} }
@ -77,6 +83,9 @@ public class UserFederationResource {
rep.setOptions(((UserFederationProviderFactory)factory).getConfigurationOptions()); rep.setOptions(((UserFederationProviderFactory)factory).getConfigurationOptions());
providers.add(rep); providers.add(rep);
} }
event.event(EventType.VIEW_FEDERATION_PROVIDERS).representation(providers).success();
return providers; return providers;
} }
@ -98,6 +107,9 @@ public class UserFederationResource {
UserFederationProviderFactoryRepresentation rep = new UserFederationProviderFactoryRepresentation(); UserFederationProviderFactoryRepresentation rep = new UserFederationProviderFactoryRepresentation();
rep.setId(factory.getId()); rep.setId(factory.getId());
rep.setOptions(((UserFederationProviderFactory)factory).getConfigurationOptions()); rep.setOptions(((UserFederationProviderFactory)factory).getConfigurationOptions());
event.event(EventType.VIEW_FEDERATION_PROVIDER).representation(rep).success();
return rep; return rep;
} }
throw new NotFoundException("Could not find provider"); throw new NotFoundException("Could not find provider");
@ -123,6 +135,10 @@ public class UserFederationResource {
new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId()); new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
checkKerberosCredential(model); checkKerberosCredential(model);
event.event(EventType.CREATE_FEDERATION_PROVIDER)
.representation(rep)
.success();
return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build(); return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
} }
@ -146,6 +162,10 @@ public class UserFederationResource {
realm.updateUserFederationProvider(model); realm.updateUserFederationProvider(model);
new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId()); new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
checkKerberosCredential(model); checkKerberosCredential(model);
event.event(EventType.UPDATE_FEDERATION_PROVIDER)
.representation(rep)
.success();
} }
/** /**
@ -161,9 +181,16 @@ public class UserFederationResource {
auth.requireView(); auth.requireView();
for (UserFederationProviderModel model : realm.getUserFederationProviders()) { for (UserFederationProviderModel model : realm.getUserFederationProviders()) {
if (model.getId().equals(id)) { if (model.getId().equals(id)) {
return ModelToRepresentation.toRepresentation(model); UserFederationProviderRepresentation rep = ModelToRepresentation.toRepresentation(model);
event.event(EventType.VIEW_FEDERATION_PROVIDER)
.representation(rep)
.success();
return rep;
} }
} }
throw new NotFoundException("could not find provider"); throw new NotFoundException("could not find provider");
} }
@ -176,9 +203,15 @@ public class UserFederationResource {
@Path("instances/{id}") @Path("instances/{id}")
public void deleteProviderInstance(@PathParam("id") String id) { public void deleteProviderInstance(@PathParam("id") String id) {
auth.requireManage(); auth.requireManage();
UserFederationProviderRepresentation rep = getProviderInstance(id);
UserFederationProviderModel model = new UserFederationProviderModel(id, null, null, -1, null, -1, -1, 0); UserFederationProviderModel model = new UserFederationProviderModel(id, null, null, -1, null, -1, -1, 0);
realm.removeUserFederationProvider(model); realm.removeUserFederationProvider(model);
new UsersSyncManager().removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), model); new UsersSyncManager().removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), model);
event.event(EventType.DELETE_FEDERATION_PROVIDER)
.representation(rep)
.success();
} }

View file

@ -7,6 +7,9 @@ import org.jboss.resteasy.spi.NotFoundException;
import org.keycloak.ClientConnection; import org.keycloak.ClientConnection;
import org.keycloak.email.EmailException; import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider; import org.keycloak.email.EmailProvider;
import org.keycloak.events.Details;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.ApplicationModel; import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel; import org.keycloak.models.ClientSessionModel;
@ -24,7 +27,6 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.protocol.oidc.utils.RedirectUtils; import org.keycloak.protocol.oidc.utils.RedirectUtils;
import org.keycloak.representations.idm.ApplicationMappingsRepresentation; import org.keycloak.representations.idm.ApplicationMappingsRepresentation;
@ -37,7 +39,6 @@ import org.keycloak.representations.idm.UserSessionRepresentation;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.managers.UserManager; import org.keycloak.services.managers.UserManager;
import org.keycloak.services.resources.flows.Flows; import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.resources.flows.Urls; import org.keycloak.services.resources.flows.Urls;
@ -57,6 +58,7 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -80,6 +82,8 @@ public class UsersResource {
private TokenManager tokenManager; private TokenManager tokenManager;
private EventBuilder event;
@Context @Context
protected ClientConnection clientConnection; protected ClientConnection clientConnection;
@ -92,10 +96,11 @@ public class UsersResource {
@Context @Context
protected HttpHeaders headers; protected HttpHeaders headers;
public UsersResource(RealmModel realm, RealmAuth auth, TokenManager tokenManager) { public UsersResource(RealmModel realm, RealmAuth auth, TokenManager tokenManager, EventBuilder event) {
this.auth = auth; this.auth = auth;
this.realm = realm; this.realm = realm;
this.tokenManager = tokenManager; this.tokenManager = tokenManager;
this.event = event;
auth.init(RealmAuth.Resource.USER); auth.init(RealmAuth.Resource.USER);
} }
@ -120,6 +125,10 @@ public class UsersResource {
} }
updateUserFromRep(user, rep); updateUserFromRep(user, rep);
event.event(EventType.UPDATE_USER)
.representation(rep)
.success();
if (session.getTransaction().isActive()) { if (session.getTransaction().isActive()) {
session.getTransaction().commit(); session.getTransaction().commit();
} }
@ -156,6 +165,10 @@ public class UsersResource {
UserModel user = session.users().addUser(realm, rep.getUsername()); UserModel user = session.users().addUser(realm, rep.getUsername());
updateUserFromRep(user, rep); updateUserFromRep(user, rep);
event.event(EventType.CREATE_USER)
.representation(rep)
.success();
if (session.getTransaction().isActive()) { if (session.getTransaction().isActive()) {
session.getTransaction().commit(); session.getTransaction().commit();
} }
@ -220,7 +233,14 @@ public class UsersResource {
if (user == null) { if (user == null) {
throw new NotFoundException("User not found"); throw new NotFoundException("User not found");
} }
return ModelToRepresentation.toRepresentation(user);
UserRepresentation rep = ModelToRepresentation.toRepresentation(user);
event.event(EventType.VIEW_USER)
.representation(rep)
.success();
return rep;
} }
/** /**
@ -245,6 +265,11 @@ public class UsersResource {
UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(session); UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(session);
reps.add(rep); reps.add(rep);
} }
event.event(EventType.VIEW_USER_SESSIONS)
.representation(reps)
.success();
return reps; return reps;
} }
@ -276,6 +301,11 @@ public class UsersResource {
} }
} }
} }
event.event(EventType.VIEW_USER)
.representation(result)
.success();
return result; return result;
} }
@ -326,10 +356,14 @@ public class UsersResource {
if (user == null) { if (user == null) {
throw new NotFoundException("User not found"); throw new NotFoundException("User not found");
} }
List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user); List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
for (UserSessionModel userSession : userSessions) { for (UserSessionModel userSession : userSessions) {
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers); AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers);
} }
event.event(EventType.LOGOUT_USER_SESSIONS)
.success();
} }
/** /**
@ -343,6 +377,7 @@ public class UsersResource {
public Response deleteUser(final @PathParam("username") String username) { public Response deleteUser(final @PathParam("username") String username) {
auth.requireManage(); auth.requireManage();
UserRepresentation rep = getUser(username);
UserModel user = session.users().getUserByUsername(username, realm); UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) { if (user == null) {
throw new NotFoundException("User not found"); throw new NotFoundException("User not found");
@ -350,6 +385,11 @@ public class UsersResource {
boolean removed = new UserManager(session).removeUser(realm, user); boolean removed = new UserManager(session).removeUser(realm, user);
if (removed) { if (removed) {
event.event(EventType.DELETE_USER)
.representation(rep)
.success();
return Response.noContent().build(); return Response.noContent().build();
} else { } else {
return Flows.errors().error("User couldn't be deleted", Response.Status.BAD_REQUEST); return Flows.errors().error("User couldn't be deleted", Response.Status.BAD_REQUEST);

View file

@ -29,6 +29,7 @@ import org.keycloak.broker.provider.AuthenticationRequest;
import org.keycloak.broker.provider.FederatedIdentity; import org.keycloak.broker.provider.FederatedIdentity;
import org.keycloak.broker.provider.IdentityBrokerException; import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.events.EventBuilder; import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventGroup;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel; import org.keycloak.models.ClientSessionModel;
@ -36,7 +37,6 @@ import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.EventsManager;
import org.keycloak.services.messages.Messages; import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.flows.Flows; import org.keycloak.services.resources.flows.Flows;
import org.keycloak.social.SocialIdentityProvider; import org.keycloak.social.SocialIdentityProvider;
@ -156,7 +156,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
} catch (Exception e) { } catch (Exception e) {
logger.error("Could get user profile from twitter.", e); logger.error("Could get user profile from twitter.", e);
} }
EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder(); EventBuilder event = new EventBuilder(EventGroup.USER, realm, session, clientConnection);
event.event(EventType.LOGIN); event.event(EventType.LOGIN);
event.error("twitter_login_failed"); event.error("twitter_login_failed");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNEXPECTED_ERROR_HANDLING_RESPONSE); return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNEXPECTED_ERROR_HANDLING_RESPONSE);

View file

@ -6,6 +6,7 @@ import org.junit.Before;
import org.junit.ClassRule; import org.junit.ClassRule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.events.Event; import org.keycloak.events.Event;
import org.keycloak.events.EventGroup;
import org.keycloak.events.EventStoreProvider; import org.keycloak.events.EventStoreProvider;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
@ -185,6 +186,7 @@ public class EventStoreProviderTest {
Event e = new Event(); Event e = new Event();
e.setTime(time); e.setTime(time);
e.setType(event); e.setType(event);
e.setGroup(EventGroup.USER);
e.setRealmId(realmId); e.setRealmId(realmId);
e.setClientId(clientId); e.setClientId(clientId);
e.setUserId(userId); e.setUserId(userId);