[KEYCLOAK-392] - Admin audit events
This commit is contained in:
parent
edfe7bd285
commit
70f53c6c06
71 changed files with 3047 additions and 257 deletions
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||
<changeSet author="giriraj.sharma27@gmail.com" id="1.3.0.Beta1">
|
||||
<createTable tableName="ADMIN_EVENT_ENTITY">
|
||||
<column name="ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="ADMIN_EVENT_TIME" type="BIGINT"/>
|
||||
<column name="OPERATION_TYPE" type="VARCHAR(255)"/>
|
||||
<column name="REALM_ID" type="VARCHAR(255)"/>
|
||||
<column name="CLIENT_ID" type="VARCHAR(255)"/>
|
||||
<column name="USER_ID" type="VARCHAR(255)"/>
|
||||
<column name="IP_ADDRESS" type="VARCHAR(255)"/>
|
||||
<column name="RESOURCE_PATH" type="VARCHAR(255)"/>
|
||||
<column name="REPRESENTATION" type="VARCHAR(25500)"/>
|
||||
<column name="ERROR" type="VARCHAR(255)"/>
|
||||
</createTable>
|
||||
<createTable tableName="REALM_ENABLED_ADMIN_EVENT_OPERATIONS">
|
||||
<column name="REALM_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="VALUE" type="VARCHAR(255)"/>
|
||||
</createTable>
|
||||
<addColumn tableName="REALM">
|
||||
<column name="ADMIN_EVENTS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="ADMIN_EVENTS_DETAILS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_ENABLED_ADMIN_EVENT_OPERATIONS" constraintName="FKF8459C8FAE5C3B34" referencedColumnNames="ID" referencedTableName="REALM"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
|
@ -5,4 +5,5 @@
|
|||
<include file="META-INF/jpa-changelog-1.1.0.Final.xml"/>
|
||||
<include file="META-INF/jpa-changelog-1.2.0.Beta1.xml"/>
|
||||
<include file="META-INF/jpa-changelog-1.2.0.RC1.xml"/>
|
||||
<include file="META-INF/jpa-changelog-1.3.0.Beta1.xml"/>
|
||||
</databaseChangeLog>
|
||||
|
|
|
@ -12,7 +12,7 @@ public interface JpaUpdaterProvider extends Provider {
|
|||
|
||||
public String FIRST_VERSION = "1.0.0.Final";
|
||||
|
||||
public String LAST_VERSION = "1.2.0.RC1";
|
||||
public String LAST_VERSION = "1.3.0.Beta1";
|
||||
|
||||
public String getCurrentVersionSql();
|
||||
|
||||
|
|
|
@ -34,9 +34,10 @@
|
|||
<class>org.keycloak.models.sessions.jpa.entities.UserSessionEntity</class>
|
||||
<class>org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity</class>
|
||||
|
||||
<!-- JpaAuditProvider -->
|
||||
<!-- JpaAuditProviders -->
|
||||
<class>org.keycloak.events.jpa.EventEntity</class>
|
||||
|
||||
<class>org.keycloak.events.jpa.AdminEventEntity</class>
|
||||
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
|
||||
<properties>
|
||||
|
|
|
@ -11,6 +11,10 @@ public class RealmEventsConfigRepresentation {
|
|||
protected Long eventsExpiration;
|
||||
protected List<String> eventsListeners;
|
||||
protected List<String> enabledEventTypes;
|
||||
|
||||
protected Boolean adminEventsEnabled;
|
||||
protected List<String> adminEnabledEventOperations;
|
||||
protected Boolean adminEventsDetailsEnabled;
|
||||
|
||||
public boolean isEventsEnabled() {
|
||||
return eventsEnabled;
|
||||
|
@ -43,4 +47,29 @@ public class RealmEventsConfigRepresentation {
|
|||
public void setEnabledEventTypes(List<String> enabledEventTypes) {
|
||||
this.enabledEventTypes = enabledEventTypes;
|
||||
}
|
||||
|
||||
public Boolean isAdminEventsEnabled() {
|
||||
return adminEventsEnabled;
|
||||
}
|
||||
|
||||
public void setAdminEventsEnabled(Boolean adminEventsEnabled) {
|
||||
this.adminEventsEnabled = adminEventsEnabled;
|
||||
}
|
||||
|
||||
public List<String> getAdminEnabledEventOperations() {
|
||||
return adminEnabledEventOperations;
|
||||
}
|
||||
|
||||
public void setAdminEnabledEventOperations(List<String> adminEnabledEventOperations) {
|
||||
this.adminEnabledEventOperations = adminEnabledEventOperations;
|
||||
}
|
||||
|
||||
public Boolean isAdminEventsDetailsEnabled() {
|
||||
return adminEventsDetailsEnabled;
|
||||
}
|
||||
|
||||
public void setAdminEventsDetailsEnabled(Boolean adminEventsDetailsEnabled) {
|
||||
this.adminEventsDetailsEnabled = adminEventsDetailsEnabled;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -57,10 +57,16 @@ public class RealmRepresentation {
|
|||
protected String accountTheme;
|
||||
protected String adminTheme;
|
||||
protected String emailTheme;
|
||||
|
||||
protected Boolean eventsEnabled;
|
||||
protected Long eventsExpiration;
|
||||
protected List<String> eventsListeners;
|
||||
protected List<String> enabledEventTypes;
|
||||
|
||||
protected Boolean adminEventsEnabled;
|
||||
protected List<String> adminEnabledEventOperations;
|
||||
protected Boolean adminEventsDetailsEnabled;
|
||||
|
||||
private List<IdentityProviderRepresentation> identityProviders;
|
||||
private List<IdentityProviderMapperRepresentation> identityProviderMappers;
|
||||
private List<ProtocolMapperRepresentation> protocolMappers;
|
||||
|
@ -507,6 +513,30 @@ public class RealmRepresentation {
|
|||
this.enabledEventTypes = enabledEventTypes;
|
||||
}
|
||||
|
||||
public Boolean isAdminEventsEnabled() {
|
||||
return adminEventsEnabled;
|
||||
}
|
||||
|
||||
public void setAdminEventsEnabled(Boolean adminEventsEnabled) {
|
||||
this.adminEventsEnabled = adminEventsEnabled;
|
||||
}
|
||||
|
||||
public List<String> getAdminEnabledEventOperations() {
|
||||
return adminEnabledEventOperations;
|
||||
}
|
||||
|
||||
public void setAdminEnabledEventOperations(List<String> adminEnabledEventOperations) {
|
||||
this.adminEnabledEventOperations = adminEnabledEventOperations;
|
||||
}
|
||||
|
||||
public Boolean isAdminEventsDetailsEnabled() {
|
||||
return adminEventsDetailsEnabled;
|
||||
}
|
||||
|
||||
public void setAdminEventsDetailsEnabled(Boolean adminEventsDetailsEnabled) {
|
||||
this.adminEventsDetailsEnabled = adminEventsDetailsEnabled;
|
||||
}
|
||||
|
||||
public List<UserFederationProviderRepresentation> getUserFederationProviders() {
|
||||
return userFederationProviders;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
package org.keycloak.events;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.ClientConnection;
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.admin.AuthDetails;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
public class AdminEventBuilder {
|
||||
|
||||
private static final Logger log = Logger.getLogger(AdminEventBuilder.class);
|
||||
|
||||
private EventStoreProvider store;
|
||||
private List<EventListenerProvider> listeners;
|
||||
private RealmModel realm;
|
||||
private AdminEvent adminEvent;
|
||||
|
||||
public AdminEventBuilder(RealmModel realm, KeycloakSession session, ClientConnection clientConnection) {
|
||||
this.realm = realm;
|
||||
|
||||
adminEvent = new AdminEvent();
|
||||
|
||||
if (realm.isAdminEventsEnabled()) {
|
||||
EventStoreProvider store = session.getProvider(EventStoreProvider.class);
|
||||
if (store != null) {
|
||||
this.store = store;
|
||||
} else {
|
||||
log.error("Admin 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);
|
||||
ipAddress(clientConnection.getRemoteAddr());
|
||||
}
|
||||
|
||||
public AdminEventBuilder operation(OperationType e) {
|
||||
adminEvent.setOperationType(e);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminEventBuilder realm(RealmModel realm) {
|
||||
AuthDetails authDetails = adminEvent.getAuthDetails();
|
||||
if(authDetails == null) {
|
||||
authDetails = new AuthDetails();
|
||||
authDetails.setRealmId(realm.getId());
|
||||
} else {
|
||||
authDetails.setRealmId(realm.getId());
|
||||
}
|
||||
adminEvent.setAuthDetails(authDetails);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminEventBuilder realm(String realmId) {
|
||||
AuthDetails authDetails = adminEvent.getAuthDetails();
|
||||
if(authDetails == null) {
|
||||
authDetails = new AuthDetails();
|
||||
authDetails.setRealmId(realmId);
|
||||
} else {
|
||||
authDetails.setRealmId(realmId);
|
||||
}
|
||||
adminEvent.setAuthDetails(authDetails);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminEventBuilder client(ClientModel client) {
|
||||
AuthDetails authDetails = adminEvent.getAuthDetails();
|
||||
if(authDetails == null) {
|
||||
authDetails = new AuthDetails();
|
||||
authDetails.setClientId(client.getId());
|
||||
} else {
|
||||
authDetails.setClientId(client.getId());
|
||||
}
|
||||
adminEvent.setAuthDetails(authDetails);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminEventBuilder client(String clientId) {
|
||||
AuthDetails authDetails = adminEvent.getAuthDetails();
|
||||
if(authDetails == null) {
|
||||
authDetails = new AuthDetails();
|
||||
authDetails.setClientId(clientId);
|
||||
} else {
|
||||
authDetails.setClientId(clientId);
|
||||
}
|
||||
adminEvent.setAuthDetails(authDetails);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminEventBuilder user(UserModel user) {
|
||||
AuthDetails authDetails = adminEvent.getAuthDetails();
|
||||
if(authDetails == null) {
|
||||
authDetails = new AuthDetails();
|
||||
authDetails.setUserId(user.getId());
|
||||
} else {
|
||||
authDetails.setUserId(user.getId());
|
||||
}
|
||||
adminEvent.setAuthDetails(authDetails);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminEventBuilder user(String userId) {
|
||||
AuthDetails authDetails = adminEvent.getAuthDetails();
|
||||
if(authDetails == null) {
|
||||
authDetails = new AuthDetails();
|
||||
authDetails.setUserId(userId);
|
||||
} else {
|
||||
authDetails.setUserId(userId);
|
||||
}
|
||||
adminEvent.setAuthDetails(authDetails);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminEventBuilder ipAddress(String ipAddress) {
|
||||
AuthDetails authDetails = adminEvent.getAuthDetails();
|
||||
if(authDetails == null) {
|
||||
authDetails = new AuthDetails();
|
||||
authDetails.setIpAddress(ipAddress);
|
||||
} else {
|
||||
authDetails.setIpAddress(ipAddress);
|
||||
}
|
||||
adminEvent.setAuthDetails(authDetails);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminEventBuilder resourcePath(String resourcePath) {
|
||||
adminEvent.setResourcePath(resourcePath);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void error(String error) {
|
||||
adminEvent.setOperationType(OperationType.valueOf(adminEvent.getOperationType().name() + "_ERROR"));
|
||||
adminEvent.setError(error);
|
||||
send();
|
||||
}
|
||||
|
||||
public AdminEventBuilder representation(Object value) {
|
||||
if (value == null || value.equals("")) {
|
||||
return this;
|
||||
}
|
||||
try {
|
||||
adminEvent.setRepresentation(JsonSerialization.writeValueAsString(value));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdminEvent getEvent() {
|
||||
return adminEvent;
|
||||
}
|
||||
|
||||
public void success() {
|
||||
send();
|
||||
}
|
||||
|
||||
private void send() {
|
||||
boolean includeRepresentation = false;
|
||||
if(realm.isAdminEventsDetailsEnabled()) {
|
||||
includeRepresentation = true;
|
||||
}
|
||||
adminEvent.setTime(Time.toMillis(Time.currentTime()));
|
||||
|
||||
if (store != null) {
|
||||
if (realm.getAdminEnabledEventOperations() != null && !realm.getAdminEnabledEventOperations().isEmpty() ? realm.getAdminEnabledEventOperations().contains(adminEvent.getOperationType().name()) : adminEvent.getOperationType().isSaveByDefault()) {
|
||||
try {
|
||||
store.onEvent(adminEvent, includeRepresentation);
|
||||
} catch (Throwable t) {
|
||||
log.error("Failed to save event", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (listeners != null) {
|
||||
for (EventListenerProvider l : listeners) {
|
||||
try {
|
||||
l.onEvent(adminEvent, includeRepresentation);
|
||||
} catch (Throwable t) {
|
||||
log.error("Failed to send type to " + l, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
|
@ -149,7 +150,7 @@ public class EventBuilder {
|
|||
}
|
||||
|
||||
private void send() {
|
||||
event.setTime(System.currentTimeMillis());
|
||||
event.setTime(Time.toMillis(Time.currentTime()));
|
||||
|
||||
if (store != null) {
|
||||
if (realm.getEnabledEventTypes() != null && !realm.getEnabledEventTypes().isEmpty() ? realm.getEnabledEventTypes().contains(event.getType().name()) : event.getType().isSaveByDefault()) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.events;
|
||||
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
/**
|
||||
|
@ -9,4 +10,6 @@ public interface EventListenerProvider extends Provider {
|
|||
|
||||
public void onEvent(Event event);
|
||||
|
||||
public void onEvent(AdminEvent event, boolean includeRepresentation);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.keycloak.events;
|
||||
|
||||
import org.keycloak.events.admin.AdminEventQuery;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
|
@ -7,10 +9,18 @@ public interface EventStoreProvider extends EventListenerProvider {
|
|||
|
||||
public EventQuery createQuery();
|
||||
|
||||
public AdminEventQuery createAdminQuery();
|
||||
|
||||
public void clear();
|
||||
|
||||
public void clear(String realmId);
|
||||
|
||||
public void clear(String realmId, long olderThan);
|
||||
|
||||
public void clearAdmin();
|
||||
|
||||
public void clearAdmin(String realmId);
|
||||
|
||||
public void clearAdmin(String realmId, long olderThan);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
package org.keycloak.events.admin;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class AdminEvent {
|
||||
|
||||
private long time;
|
||||
|
||||
private AuthDetails authDetails;
|
||||
|
||||
private OperationType operationType;
|
||||
|
||||
private String resourcePath;
|
||||
|
||||
private String representation;
|
||||
|
||||
private String error;
|
||||
|
||||
/**
|
||||
* Returns the time of the event
|
||||
*
|
||||
* @return time in millis
|
||||
*/
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(long time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns authentication details
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public AuthDetails getAuthDetails() {
|
||||
return authDetails;
|
||||
}
|
||||
|
||||
public void setAuthDetails(AuthDetails authDetails) {
|
||||
this.authDetails = authDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the operation
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public OperationType getOperationType() {
|
||||
return operationType;
|
||||
}
|
||||
|
||||
public void setOperationType(OperationType operationType) {
|
||||
this.operationType = operationType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path of the resource. For example:
|
||||
* <ul>
|
||||
* <li><b>realms</b> - realm list</li>
|
||||
* <li><b>realms/master</b> - master realm</li>
|
||||
* <li><b>realms/clients/00d4b16f-f1f9-4e73-8366-d76b18f3e0e1</b> - client within the master realm</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getResourcePath() {
|
||||
return resourcePath;
|
||||
}
|
||||
|
||||
public void setResourcePath(String resourcePath) {
|
||||
this.resourcePath = resourcePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the updated JSON representation if <code>operationType</code> is <code>CREATE</code> or <code>UPDATE</code>.
|
||||
* Otherwise returns <code>null</code>.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getRepresentation() {
|
||||
return representation;
|
||||
}
|
||||
|
||||
public void setRepresentation(String representation) {
|
||||
this.representation = representation;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the event was unsuccessful returns the error message. Otherwise returns <code>null</code>.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setError(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package org.keycloak.events.admin;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface AdminEventQuery {
|
||||
|
||||
/**
|
||||
* Search by authentication realm
|
||||
*
|
||||
* @param realm realm name
|
||||
* @return Associated <code>AdminEventQuery</code> for method chaining
|
||||
*/
|
||||
AdminEventQuery authRealm(String realm);
|
||||
|
||||
/**
|
||||
* Search by authenticated client
|
||||
*
|
||||
* @param client client uuid
|
||||
* @return Associated <code>AdminEventQuery</code> for method chaining
|
||||
*/
|
||||
AdminEventQuery authClient(String client);
|
||||
|
||||
/**
|
||||
* Search by authenticated user
|
||||
*
|
||||
* @param user user uuid
|
||||
* @return Associated <code>AdminEventQuery</code> for method chaining
|
||||
*/
|
||||
AdminEventQuery authUser(String user);
|
||||
|
||||
/**
|
||||
* Search by request ip address
|
||||
*
|
||||
* @param ipAddress
|
||||
* @return Associated <code>AdminEventQuery</code> for method chaining
|
||||
*/
|
||||
AdminEventQuery authIpAddress(String ipAddress);
|
||||
|
||||
/**
|
||||
* Search by operation type
|
||||
*
|
||||
* @param operations
|
||||
* @return <code>this</code> for method chaining
|
||||
*/
|
||||
AdminEventQuery operation(OperationType... operations);
|
||||
|
||||
/**
|
||||
* Search by resource path. Supports wildcards <code>*</code> and <code>**</code>. For example:
|
||||
* <ul>
|
||||
* <li><b>*/master</b> - matches 'realms/master'</li>
|
||||
* <li><b>**/00d4b16f</b> - matches 'realms/master/clients/00d4b16f'</li>
|
||||
* <li><b>realms/master/**</b> - matches anything under 'realms/master'</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param resourcePath
|
||||
* @return <code>this</code> for method chaining
|
||||
*/
|
||||
AdminEventQuery resourcePath(String resourcePath);
|
||||
|
||||
/**
|
||||
* Search by events after the specified time
|
||||
*
|
||||
* @param fromTime time in millis
|
||||
* @return <code>this</code> for method chaining
|
||||
*/
|
||||
AdminEventQuery fromTime(String fromTime);
|
||||
|
||||
/**
|
||||
* Search by events before the specified time
|
||||
*
|
||||
* @param toTime time in millis
|
||||
* @return <code>this</code> for method chaining
|
||||
*/
|
||||
AdminEventQuery toTime(String toTime);
|
||||
|
||||
/**
|
||||
* Used for pagination
|
||||
*
|
||||
* @param first first result to return
|
||||
* @return <code>this</code> for method chaining
|
||||
*/
|
||||
AdminEventQuery firstResult(int first);
|
||||
|
||||
/**
|
||||
* Use for pagination
|
||||
*
|
||||
* @param max the maximum results to return
|
||||
* @return <code>this</code> for method chaining
|
||||
*/
|
||||
AdminEventQuery maxResults(int max);
|
||||
|
||||
/**
|
||||
* Executes the query and returns the results
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<AdminEvent> getResultList();
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package org.keycloak.events.admin;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class AuthDetails {
|
||||
|
||||
private String realmId;
|
||||
|
||||
private String clientId;
|
||||
|
||||
private String userId;
|
||||
|
||||
private String ipAddress;
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package org.keycloak.events.admin;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class AuthQuery {
|
||||
|
||||
private String realmId;
|
||||
|
||||
private String clientId;
|
||||
|
||||
private String userId;
|
||||
|
||||
private String ipAddress;
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
}
|
24
events/api/src/main/java/org/keycloak/events/admin/OperationType.java
Executable file
24
events/api/src/main/java/org/keycloak/events/admin/OperationType.java
Executable file
|
@ -0,0 +1,24 @@
|
|||
package org.keycloak.events.admin;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public enum OperationType {
|
||||
|
||||
VIEW(false),
|
||||
CREATE(true),
|
||||
UPDATE(true),
|
||||
DELETE(true),
|
||||
ACTION(false);
|
||||
|
||||
private boolean saveByDefault;
|
||||
|
||||
OperationType(boolean saveByDefault) {
|
||||
this.saveByDefault = saveByDefault;
|
||||
}
|
||||
|
||||
public boolean isSaveByDefault() {
|
||||
return saveByDefault;
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,7 @@ package org.keycloak.events.email;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.email.EmailException;
|
||||
import org.keycloak.email.EmailProvider;
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventListenerProvider;
|
||||
import org.keycloak.events.EventType;
|
||||
|
@ -49,6 +50,11 @@ public class EmailEventListenerProvider implements EventListenerProvider {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(AdminEvent event, boolean includeRepresentation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.keycloak.events.log;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventListenerProvider;
|
||||
import org.keycloak.models.KeycloakContext;
|
||||
|
@ -9,8 +10,8 @@ import org.keycloak.models.KeycloakSession;
|
|||
import javax.ws.rs.core.Cookie;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -66,29 +67,42 @@ public class JBossLoggingEventListenerProvider implements EventListenerProvider
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(logger.isTraceEnabled()) {
|
||||
setKeycloakContext(sb);
|
||||
}
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
KeycloakContext context = session.getContext();
|
||||
UriInfo uriInfo = context.getUri();
|
||||
HttpHeaders headers = context.getRequestHeaders();
|
||||
if (uriInfo != null) {
|
||||
sb.append(", requestUri=");
|
||||
sb.append(uriInfo.getRequestUri().toString());
|
||||
}
|
||||
logger.log(logger.isTraceEnabled() ? Logger.Level.TRACE : level, sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
if (headers != null) {
|
||||
sb.append(", cookies=[");
|
||||
boolean f = true;
|
||||
for (Map.Entry<String, Cookie> e : headers.getCookies().entrySet()) {
|
||||
if (f) {
|
||||
f = false;
|
||||
} else {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(e.getValue().toString());
|
||||
}
|
||||
sb.append("]");
|
||||
}
|
||||
@Override
|
||||
public void onEvent(AdminEvent adminEvent, boolean includeRepresentation) {
|
||||
Logger.Level level = adminEvent.getError() != null ? errorLevel : successLevel;
|
||||
|
||||
if (logger.isEnabled(level)) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("operationType=");
|
||||
sb.append(adminEvent.getOperationType());
|
||||
sb.append(", realmId=");
|
||||
sb.append(adminEvent.getAuthDetails().getRealmId());
|
||||
sb.append(", clientId=");
|
||||
sb.append(adminEvent.getAuthDetails().getClientId());
|
||||
sb.append(", userId=");
|
||||
sb.append(adminEvent.getAuthDetails().getUserId());
|
||||
sb.append(", ipAddress=");
|
||||
sb.append(adminEvent.getAuthDetails().getIpAddress());
|
||||
sb.append(", resourcePath=");
|
||||
sb.append(adminEvent.getResourcePath());
|
||||
|
||||
if (adminEvent.getError() != null) {
|
||||
sb.append(", error=");
|
||||
sb.append(adminEvent.getError());
|
||||
}
|
||||
|
||||
if(logger.isTraceEnabled()) {
|
||||
setKeycloakContext(sb);
|
||||
}
|
||||
|
||||
logger.log(logger.isTraceEnabled() ? Logger.Level.TRACE : level, sb.toString());
|
||||
|
@ -98,5 +112,30 @@ public class JBossLoggingEventListenerProvider implements EventListenerProvider
|
|||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
private void setKeycloakContext(StringBuilder sb) {
|
||||
KeycloakContext context = session.getContext();
|
||||
UriInfo uriInfo = context.getUri();
|
||||
HttpHeaders headers = context.getRequestHeaders();
|
||||
if (uriInfo != null) {
|
||||
sb.append(", requestUri=");
|
||||
sb.append(uriInfo.getRequestUri().toString());
|
||||
}
|
||||
|
||||
if (headers != null) {
|
||||
sb.append(", cookies=[");
|
||||
boolean f = true;
|
||||
for (Map.Entry<String, Cookie> e : headers.getCookies().entrySet()) {
|
||||
if (f) {
|
||||
f = false;
|
||||
} else {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(e.getValue().toString());
|
||||
}
|
||||
sb.append("]");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
package org.keycloak.events.jpa;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:giriraj.sharma27@gmail.com">Giriraj Sharma</a>
|
||||
*/
|
||||
@Entity
|
||||
@Table(name="ADMIN_EVENT_ENTITY")
|
||||
public class AdminEventEntity {
|
||||
|
||||
@Id
|
||||
@Column(name="ID", length = 36)
|
||||
private String id;
|
||||
|
||||
@Column(name="ADMIN_EVENT_TIME")
|
||||
private long time;
|
||||
|
||||
@Column(name="OPERATION_TYPE")
|
||||
private String operationType;
|
||||
|
||||
@Column(name="REALM_ID")
|
||||
private String authRealmId;
|
||||
|
||||
@Column(name="CLIENT_ID")
|
||||
private String authClientId;
|
||||
|
||||
@Column(name="USER_ID")
|
||||
private String authUserId;
|
||||
|
||||
@Column(name="IP_ADDRESS")
|
||||
private String authIpAddress;
|
||||
|
||||
@Column(name="RESOURCE_PATH")
|
||||
private String resourcePath;
|
||||
|
||||
@Column(name="REPRESENTATION", length = 25500)
|
||||
private String representation;
|
||||
|
||||
@Column(name="ERROR")
|
||||
private String error;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(long time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public String getOperationType() {
|
||||
return operationType;
|
||||
}
|
||||
|
||||
public void setOperationType(String operationType) {
|
||||
this.operationType = operationType;
|
||||
}
|
||||
|
||||
public String getAuthRealmId() {
|
||||
return authRealmId;
|
||||
}
|
||||
|
||||
public void setAuthRealmId(String authRealmId) {
|
||||
this.authRealmId = authRealmId;
|
||||
}
|
||||
|
||||
public String getAuthClientId() {
|
||||
return authClientId;
|
||||
}
|
||||
|
||||
public void setAuthClientId(String authClientId) {
|
||||
this.authClientId = authClientId;
|
||||
}
|
||||
|
||||
public String getAuthUserId() {
|
||||
return authUserId;
|
||||
}
|
||||
|
||||
public void setAuthUserId(String authUserId) {
|
||||
this.authUserId = authUserId;
|
||||
}
|
||||
|
||||
public String getAuthIpAddress() {
|
||||
return authIpAddress;
|
||||
}
|
||||
|
||||
public void setAuthIpAddress(String authIpAddress) {
|
||||
this.authIpAddress = authIpAddress;
|
||||
}
|
||||
|
||||
public String getResourcePath() {
|
||||
return resourcePath;
|
||||
}
|
||||
|
||||
public void setResourcePath(String resourcePath) {
|
||||
this.resourcePath = resourcePath;
|
||||
}
|
||||
|
||||
public String getRepresentation() {
|
||||
return representation;
|
||||
}
|
||||
|
||||
public void setRepresentation(String representation) {
|
||||
this.representation = representation;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setError(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
package org.keycloak.events.jpa;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.admin.AdminEventQuery;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:giriraj.sharma27@gmail.com">Giriraj Sharma</a>
|
||||
*/
|
||||
public class JpaAdminEventQuery implements AdminEventQuery {
|
||||
|
||||
private final EntityManager em;
|
||||
private final CriteriaBuilder cb;
|
||||
private final CriteriaQuery<AdminEventEntity> cq;
|
||||
private final Root<AdminEventEntity> root;
|
||||
private final ArrayList<Predicate> predicates;
|
||||
private Integer firstResult;
|
||||
private Integer maxResults;
|
||||
|
||||
public JpaAdminEventQuery(EntityManager em) {
|
||||
this.em = em;
|
||||
|
||||
cb = em.getCriteriaBuilder();
|
||||
cq = cb.createQuery(AdminEventEntity.class);
|
||||
root = cq.from(AdminEventEntity.class);
|
||||
predicates = new ArrayList<Predicate>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery operation(OperationType... operations) {
|
||||
List<String> operationStrings = new LinkedList<String>();
|
||||
for (OperationType e : operations) {
|
||||
operationStrings.add(e.toString());
|
||||
}
|
||||
predicates.add(root.get("operationType").in(operationStrings));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery authRealm(String realmId) {
|
||||
predicates.add(cb.equal(root.get("authRealmId"), realmId));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery authClient(String clientId) {
|
||||
predicates.add(cb.equal(root.get("authClientId"), clientId));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery authUser(String userId) {
|
||||
predicates.add(cb.equal(root.get("authUserId"), userId));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery authIpAddress(String ipAddress) {
|
||||
predicates.add(cb.equal(root.get("authIpAddress"), ipAddress));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery resourcePath(String resourcePath) {
|
||||
Expression<String> rPath = root.get("resourcePath");
|
||||
predicates.add(cb.like(rPath, "%"+resourcePath+"%"));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery fromTime(String fromTime) {
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Long from = null;
|
||||
try {
|
||||
from = df.parse(fromTime).getTime();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
predicates.add(cb.greaterThanOrEqualTo(root.<Long>get("time"), from));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery toTime(String toTime) {
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Long to = null;
|
||||
try {
|
||||
to = df.parse(toTime).getTime();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
predicates.add(cb.lessThanOrEqualTo(root.<Long>get("time"), to));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery firstResult(int firstResult) {
|
||||
this.firstResult = firstResult;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery maxResults(int maxResults) {
|
||||
this.maxResults = maxResults;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AdminEvent> getResultList() {
|
||||
if (!predicates.isEmpty()) {
|
||||
cq.where(cb.and(predicates.toArray(new Predicate[predicates.size()])));
|
||||
}
|
||||
|
||||
cq.orderBy(cb.desc(root.get("time")));
|
||||
|
||||
TypedQuery<AdminEventEntity> query = em.createQuery(cq);
|
||||
|
||||
if (firstResult != null) {
|
||||
query.setFirstResult(firstResult);
|
||||
}
|
||||
|
||||
if (maxResults != null) {
|
||||
query.setMaxResults(maxResults);
|
||||
}
|
||||
|
||||
List<AdminEvent> events = new LinkedList<AdminEvent>();
|
||||
for (AdminEventEntity e : query.getResultList()) {
|
||||
events.add(JpaEventStoreProvider.convertAdminEvent(e));
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
}
|
|
@ -131,7 +131,7 @@ public class JpaEventQuery implements EventQuery {
|
|||
|
||||
List<Event> events = new LinkedList<Event>();
|
||||
for (EventEntity e : query.getResultList()) {
|
||||
events.add(JpaEventStoreProvider.convert(e));
|
||||
events.add(JpaEventStoreProvider.convertEvent(e));
|
||||
}
|
||||
|
||||
return events;
|
||||
|
|
|
@ -3,12 +3,17 @@ package org.keycloak.events.jpa;
|
|||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.codehaus.jackson.type.TypeReference;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.admin.AdminEventQuery;
|
||||
import org.keycloak.events.admin.AuthDetails;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventQuery;
|
||||
import org.keycloak.events.EventStoreProvider;
|
||||
import org.keycloak.events.EventType;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
@ -51,49 +56,119 @@ public class JpaEventStoreProvider implements EventStoreProvider {
|
|||
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
em.persist(convert(event));
|
||||
em.persist(convertEvent(event));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery createAdminQuery() {
|
||||
return new JpaAdminEventQuery(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAdmin() {
|
||||
em.createQuery("delete from AdminEventEntity").executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAdmin(String authRealmId) {
|
||||
em.createQuery("delete from AdminEventEntity where authRealmId = :authRealmId").setParameter("authRealmId", authRealmId).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAdmin(String authRealmId, long olderThan) {
|
||||
em.createQuery("delete from AdminEventEntity where authRealmId = :authRealmId and time < :time").setParameter("authRealmId", authRealmId).setParameter("time", olderThan).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(AdminEvent event, boolean includeRepresentation) {
|
||||
em.persist(convertAdminEvent(event, includeRepresentation));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
static EventEntity convert(Event o) {
|
||||
EventEntity e = new EventEntity();
|
||||
e.setId(UUID.randomUUID().toString());
|
||||
e.setTime(o.getTime());
|
||||
e.setType(o.getType().toString());
|
||||
e.setRealmId(o.getRealmId());
|
||||
e.setClientId(o.getClientId());
|
||||
e.setUserId(o.getUserId());
|
||||
e.setSessionId(o.getSessionId());
|
||||
e.setIpAddress(o.getIpAddress());
|
||||
e.setError(o.getError());
|
||||
static EventEntity convertEvent(Event event) {
|
||||
EventEntity eventEntity = new EventEntity();
|
||||
eventEntity.setId(UUID.randomUUID().toString());
|
||||
eventEntity.setTime(event.getTime());
|
||||
eventEntity.setType(event.getType().toString());
|
||||
eventEntity.setRealmId(event.getRealmId());
|
||||
eventEntity.setClientId(event.getClientId());
|
||||
eventEntity.setUserId(event.getUserId());
|
||||
eventEntity.setSessionId(event.getSessionId());
|
||||
eventEntity.setIpAddress(event.getIpAddress());
|
||||
eventEntity.setError(event.getError());
|
||||
try {
|
||||
e.setDetailsJson(mapper.writeValueAsString(o.getDetails()));
|
||||
eventEntity.setDetailsJson(mapper.writeValueAsString(event.getDetails()));
|
||||
} catch (IOException ex) {
|
||||
logger.error("Failed to write log details", ex);
|
||||
}
|
||||
return e;
|
||||
return eventEntity;
|
||||
}
|
||||
|
||||
static Event convert(EventEntity o) {
|
||||
Event e = new Event();
|
||||
e.setTime(o.getTime());
|
||||
e.setType(EventType.valueOf(o.getType()));
|
||||
e.setRealmId(o.getRealmId());
|
||||
e.setClientId(o.getClientId());
|
||||
e.setUserId(o.getUserId());
|
||||
e.setSessionId(o.getSessionId());
|
||||
e.setIpAddress(o.getIpAddress());
|
||||
e.setError(o.getError());
|
||||
static Event convertEvent(EventEntity eventEntity) {
|
||||
Event event = new Event();
|
||||
event.setTime(eventEntity.getTime());
|
||||
event.setType(EventType.valueOf(eventEntity.getType()));
|
||||
event.setRealmId(eventEntity.getRealmId());
|
||||
event.setClientId(eventEntity.getClientId());
|
||||
event.setUserId(eventEntity.getUserId());
|
||||
event.setSessionId(eventEntity.getSessionId());
|
||||
event.setIpAddress(eventEntity.getIpAddress());
|
||||
event.setError(eventEntity.getError());
|
||||
try {
|
||||
Map<String, String> details = mapper.readValue(o.getDetailsJson(), mapType);
|
||||
e.setDetails(details);
|
||||
Map<String, String> details = mapper.readValue(eventEntity.getDetailsJson(), mapType);
|
||||
event.setDetails(details);
|
||||
} catch (IOException ex) {
|
||||
logger.error("Failed to read log details", ex);
|
||||
}
|
||||
return e;
|
||||
return event;
|
||||
}
|
||||
|
||||
static AdminEventEntity convertAdminEvent(AdminEvent adminEvent, boolean includeRepresentation) {
|
||||
AdminEventEntity adminEventEntity = new AdminEventEntity();
|
||||
adminEventEntity.setId(UUID.randomUUID().toString());
|
||||
adminEventEntity.setTime(adminEvent.getTime());
|
||||
setAuthDetails(adminEventEntity, adminEvent.getAuthDetails());
|
||||
adminEventEntity.setOperationType(adminEvent.getOperationType().toString());
|
||||
adminEventEntity.setResourcePath(adminEvent.getResourcePath());
|
||||
adminEventEntity.setError(adminEvent.getError());
|
||||
|
||||
if(includeRepresentation) {
|
||||
adminEventEntity.setRepresentation(adminEvent.getRepresentation());
|
||||
}
|
||||
return adminEventEntity;
|
||||
}
|
||||
|
||||
static AdminEvent convertAdminEvent(AdminEventEntity adminEventEntity) {
|
||||
AdminEvent adminEvent = new AdminEvent();
|
||||
adminEvent.setTime(adminEventEntity.getTime());
|
||||
setAuthDetails(adminEvent, adminEventEntity);
|
||||
adminEvent.setOperationType(OperationType.valueOf(adminEventEntity.getOperationType()));
|
||||
adminEvent.setResourcePath(adminEventEntity.getResourcePath());
|
||||
adminEvent.setError(adminEventEntity.getError());
|
||||
|
||||
if(adminEventEntity.getRepresentation() != null) {
|
||||
adminEvent.setRepresentation(adminEventEntity.getRepresentation());
|
||||
}
|
||||
return adminEvent;
|
||||
}
|
||||
|
||||
private static void setAuthDetails(AdminEventEntity adminEventEntity, AuthDetails authDetails) {
|
||||
adminEventEntity.setAuthRealmId(authDetails.getRealmId());
|
||||
adminEventEntity.setAuthClientId(authDetails.getClientId());
|
||||
adminEventEntity.setAuthUserId(authDetails.getUserId());
|
||||
adminEventEntity.setAuthIpAddress(authDetails.getIpAddress());
|
||||
}
|
||||
|
||||
private static void setAuthDetails(AdminEvent adminEvent, AdminEventEntity adminEventEntity) {
|
||||
AuthDetails authDetails = new AuthDetails();
|
||||
authDetails.setRealmId(adminEventEntity.getAuthRealmId());
|
||||
authDetails.setClientId(adminEventEntity.getAuthClientId());
|
||||
authDetails.setUserId(adminEventEntity.getAuthUserId());
|
||||
authDetails.setIpAddress(adminEventEntity.getAuthIpAddress());
|
||||
adminEvent.setAuthDetails(authDetails);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
package org.keycloak.events.mongo;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.admin.AdminEventQuery;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
|
||||
public class MongoAdminEventQuery implements AdminEventQuery{
|
||||
|
||||
private Integer firstResult;
|
||||
private Integer maxResults;
|
||||
private DBCollection audit;
|
||||
private final BasicDBObject query;
|
||||
|
||||
public MongoAdminEventQuery(DBCollection audit) {
|
||||
this.audit = audit;
|
||||
query = new BasicDBObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery operation(OperationType... operations) {
|
||||
List<String> operationStrings = new LinkedList<String>();
|
||||
for (OperationType e : operations) {
|
||||
operationStrings.add(e.toString());
|
||||
}
|
||||
query.put("operationType", new BasicDBObject("$in", operationStrings));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery authRealm(String realmId) {
|
||||
query.put("realmId", realmId);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery authClient(String clientId) {
|
||||
query.put("clientId", clientId);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery authUser(String userId) {
|
||||
query.put("userId", userId);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery authIpAddress(String ipAddress) {
|
||||
query.put("ipAddress", ipAddress);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery resourcePath(String resourcePath) {
|
||||
query.put("resourcePath", Pattern.compile(resourcePath));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery fromTime(String fromTime) {
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Long from = null;
|
||||
try {
|
||||
from = df.parse(fromTime).getTime();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
query.put("time", BasicDBObjectBuilder.start("$gte", from).get());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery toTime(String toTime) {
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Long to = null;
|
||||
try {
|
||||
to = df.parse(toTime).getTime();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
query.put("time", BasicDBObjectBuilder.start("$lte", to).get());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery firstResult(int firstResult) {
|
||||
this.firstResult = firstResult;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery maxResults(int maxResults) {
|
||||
this.maxResults = maxResults;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AdminEvent> getResultList() {
|
||||
DBCursor cur = audit.find(query).sort(new BasicDBObject("time", -1));
|
||||
if (firstResult != null) {
|
||||
cur.skip(firstResult);
|
||||
}
|
||||
if (maxResults != null) {
|
||||
cur.limit(maxResults);
|
||||
}
|
||||
|
||||
List<AdminEvent> events = new LinkedList<AdminEvent>();
|
||||
while (cur.hasNext()) {
|
||||
events.add(MongoEventStoreProvider.convertAdminEvent((BasicDBObject) cur.next()));
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
}
|
|
@ -118,7 +118,7 @@ public class MongoEventQuery implements EventQuery {
|
|||
|
||||
List<Event> events = new LinkedList<Event>();
|
||||
while (cur.hasNext()) {
|
||||
events.add(MongoEventStoreProvider.convert((BasicDBObject) cur.next()));
|
||||
events.add(MongoEventStoreProvider.convertEvent((BasicDBObject) cur.next()));
|
||||
}
|
||||
|
||||
return events;
|
||||
|
|
|
@ -3,6 +3,11 @@ package org.keycloak.events.mongo;
|
|||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.admin.AdminEventQuery;
|
||||
import org.keycloak.events.admin.AuthDetails;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventQuery;
|
||||
import org.keycloak.events.EventStoreProvider;
|
||||
|
@ -15,11 +20,13 @@ import java.util.Map;
|
|||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class MongoEventStoreProvider implements EventStoreProvider {
|
||||
|
||||
|
||||
private DBCollection events;
|
||||
private DBCollection adminEvents;
|
||||
|
||||
public MongoEventStoreProvider(DBCollection events) {
|
||||
public MongoEventStoreProvider(DBCollection events, DBCollection adminEvents) {
|
||||
this.events = events;
|
||||
this.adminEvents = adminEvents;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -47,27 +54,55 @@ public class MongoEventStoreProvider implements EventStoreProvider {
|
|||
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
events.insert(convert(event));
|
||||
events.insert(convertEvent(event));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery createAdminQuery() {
|
||||
return new MongoAdminEventQuery(adminEvents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAdmin() {
|
||||
adminEvents.remove(new BasicDBObject());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAdmin(String realmId) {
|
||||
adminEvents.remove(new BasicDBObject("realmId", realmId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAdmin(String realmId, long olderThan) {
|
||||
BasicDBObject q = new BasicDBObject();
|
||||
q.put("realmId", realmId);
|
||||
q.put("time", new BasicDBObject("$lt", olderThan));
|
||||
adminEvents.remove(q);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(AdminEvent adminEvent, boolean includeRepresentation) {
|
||||
adminEvents.insert(convertAdminEvent(adminEvent, includeRepresentation));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
static DBObject convert(Event o) {
|
||||
static DBObject convertEvent(Event event) {
|
||||
BasicDBObject e = new BasicDBObject();
|
||||
e.put("time", o.getTime());
|
||||
e.put("type", o.getType().toString());
|
||||
e.put("realmId", o.getRealmId());
|
||||
e.put("clientId", o.getClientId());
|
||||
e.put("userId", o.getUserId());
|
||||
e.put("sessionId", o.getSessionId());
|
||||
e.put("ipAddress", o.getIpAddress());
|
||||
e.put("error", o.getError());
|
||||
e.put("time", event.getTime());
|
||||
e.put("type", event.getType().toString());
|
||||
e.put("realmId", event.getRealmId());
|
||||
e.put("clientId", event.getClientId());
|
||||
e.put("userId", event.getUserId());
|
||||
e.put("sessionId", event.getSessionId());
|
||||
e.put("ipAddress", event.getIpAddress());
|
||||
e.put("error", event.getError());
|
||||
|
||||
BasicDBObject details = new BasicDBObject();
|
||||
if (o.getDetails() != null) {
|
||||
for (Map.Entry<String, String> entry : o.getDetails().entrySet()) {
|
||||
if (event.getDetails() != null) {
|
||||
for (Map.Entry<String, String> entry : event.getDetails().entrySet()) {
|
||||
details.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
@ -76,16 +111,16 @@ public class MongoEventStoreProvider implements EventStoreProvider {
|
|||
return e;
|
||||
}
|
||||
|
||||
static Event convert(BasicDBObject o) {
|
||||
Event e = new Event();
|
||||
e.setTime(o.getLong("time"));
|
||||
e.setType(EventType.valueOf(o.getString("type")));
|
||||
e.setRealmId(o.getString("realmId"));
|
||||
e.setClientId(o.getString("clientId"));
|
||||
e.setUserId(o.getString("userId"));
|
||||
e.setSessionId(o.getString("sessionId"));
|
||||
e.setIpAddress(o.getString("ipAddress"));
|
||||
e.setError(o.getString("error"));
|
||||
static Event convertEvent(BasicDBObject o) {
|
||||
Event event = new Event();
|
||||
event.setTime(o.getLong("time"));
|
||||
event.setType(EventType.valueOf(o.getString("type")));
|
||||
event.setRealmId(o.getString("realmId"));
|
||||
event.setClientId(o.getString("clientId"));
|
||||
event.setUserId(o.getString("userId"));
|
||||
event.setSessionId(o.getString("sessionId"));
|
||||
event.setIpAddress(o.getString("ipAddress"));
|
||||
event.setError(o.getString("error"));
|
||||
|
||||
BasicDBObject d = (BasicDBObject) o.get("details");
|
||||
if (d != null) {
|
||||
|
@ -93,10 +128,55 @@ public class MongoEventStoreProvider implements EventStoreProvider {
|
|||
for (Object k : d.keySet()) {
|
||||
details.put((String) k, d.getString((String) k));
|
||||
}
|
||||
e.setDetails(details);
|
||||
event.setDetails(details);
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
private static DBObject convertAdminEvent(AdminEvent adminEvent, boolean includeRepresentation) {
|
||||
BasicDBObject e = new BasicDBObject();
|
||||
e.put("time", adminEvent.getTime());
|
||||
e.put("operationType", adminEvent.getOperationType().toString());
|
||||
setAuthDetails(e, adminEvent.getAuthDetails());
|
||||
e.put("resourcePath", adminEvent.getResourcePath());
|
||||
e.put("error", adminEvent.getError());
|
||||
|
||||
if(includeRepresentation) {
|
||||
e.put("representation", adminEvent.getRepresentation());
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static AdminEvent convertAdminEvent(BasicDBObject o) {
|
||||
AdminEvent adminEvent = new AdminEvent();
|
||||
adminEvent.setTime(o.getLong("time"));
|
||||
adminEvent.setOperationType(OperationType.valueOf(o.getString("operationType")));
|
||||
setAuthDetails(adminEvent, o);
|
||||
adminEvent.setResourcePath(o.getString("resourcePath"));
|
||||
adminEvent.setError(o.getString("error"));
|
||||
|
||||
if(o.getString("representation") != null) {
|
||||
adminEvent.setRepresentation(o.getString("representation"));
|
||||
}
|
||||
return adminEvent;
|
||||
}
|
||||
|
||||
private static void setAuthDetails(BasicDBObject e, AuthDetails authDetails) {
|
||||
e.put("realmId", authDetails.getRealmId());
|
||||
e.put("clientId", authDetails.getClientId());
|
||||
e.put("userId", authDetails.getUserId());
|
||||
e.put("ipAddress", authDetails.getIpAddress());
|
||||
}
|
||||
|
||||
private static void setAuthDetails(AdminEvent adminEvent, BasicDBObject o) {
|
||||
AuthDetails authDetails = new AuthDetails();
|
||||
authDetails.setRealmId(o.getString("realmId"));
|
||||
authDetails.setClientId(o.getString("clientId"));
|
||||
authDetails.setUserId(o.getString("userId"));
|
||||
authDetails.setIpAddress(o.getString("ipAddress"));
|
||||
adminEvent.setAuthDetails(authDetails);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,9 +24,12 @@ public class MongoEventStoreProviderFactory implements EventStoreProviderFactory
|
|||
MongoConnectionProvider connection = session.getProvider(MongoConnectionProvider.class);
|
||||
|
||||
DBCollection collection = connection.getDB().getCollection("events");
|
||||
DBCollection adminCollection = connection.getDB().getCollection("adminEvents");
|
||||
|
||||
collection.setWriteConcern(WriteConcern.UNACKNOWLEDGED);
|
||||
adminCollection.setWriteConcern(WriteConcern.UNACKNOWLEDGED);
|
||||
|
||||
return new MongoEventStoreProvider(collection);
|
||||
return new MongoEventStoreProvider(collection, adminCollection);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.events.log;
|
||||
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventListenerProvider;
|
||||
import org.productivity.java.syslog4j.SyslogConstants;
|
||||
|
@ -58,6 +59,33 @@ public class SysLoggingEventListenerProvider implements EventListenerProvider {
|
|||
syslogger.log(level, sb.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(AdminEvent adminEvent, boolean includeRepresentation) {
|
||||
int level = adminEvent.getError() != null ? SyslogConstants.LEVEL_ERROR : SyslogConstants.LEVEL_INFO;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("operationType=");
|
||||
sb.append(adminEvent.getOperationType());
|
||||
sb.append(", realmId=");
|
||||
sb.append(adminEvent.getAuthDetails().getRealmId());
|
||||
sb.append(", clientId=");
|
||||
sb.append(adminEvent.getAuthDetails().getClientId());
|
||||
sb.append(", userId=");
|
||||
sb.append(adminEvent.getAuthDetails().getUserId());
|
||||
sb.append(", ipAddress=");
|
||||
sb.append(adminEvent.getAuthDetails().getIpAddress());
|
||||
sb.append(", resourcePath=");
|
||||
sb.append(adminEvent.getResourcePath());
|
||||
|
||||
if (adminEvent.getError() != null) {
|
||||
sb.append(", error=");
|
||||
sb.append(adminEvent.getError());
|
||||
}
|
||||
|
||||
syslogger.log(level, sb.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.keycloak.examples.providers.events;
|
||||
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventListenerProvider;
|
||||
import org.keycloak.events.EventType;
|
||||
|
@ -13,9 +15,11 @@ import java.util.Set;
|
|||
public class SysoutEventListenerProvider implements EventListenerProvider {
|
||||
|
||||
private Set<EventType> excludedEvents;
|
||||
private Set<OperationType> excludedAdminOperations;
|
||||
|
||||
public SysoutEventListenerProvider(Set<EventType> excludedEvents) {
|
||||
public SysoutEventListenerProvider(Set<EventType> excludedEvents, Set<OperationType> excludedAdminOpearations) {
|
||||
this.excludedEvents = excludedEvents;
|
||||
this.excludedAdminOperations = excludedAdminOpearations;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -28,6 +32,16 @@ public class SysoutEventListenerProvider implements EventListenerProvider {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(AdminEvent event, boolean includeRepresentation) {
|
||||
// Ignore excluded operations
|
||||
if (excludedAdminOperations != null && excludedAdminOperations.contains(event.getOperationType())) {
|
||||
return;
|
||||
} else {
|
||||
System.out.println("EVENT: " + toString(event));
|
||||
}
|
||||
}
|
||||
|
||||
private String toString(Event event) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
|
@ -64,7 +78,31 @@ public class SysoutEventListenerProvider implements EventListenerProvider {
|
|||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String toString(AdminEvent adminEvent) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("operationType=");
|
||||
sb.append(adminEvent.getOperationType());
|
||||
sb.append(", realmId=");
|
||||
sb.append(adminEvent.getAuthDetails().getRealmId());
|
||||
sb.append(", clientId=");
|
||||
sb.append(adminEvent.getAuthDetails().getClientId());
|
||||
sb.append(", userId=");
|
||||
sb.append(adminEvent.getAuthDetails().getUserId());
|
||||
sb.append(", ipAddress=");
|
||||
sb.append(adminEvent.getAuthDetails().getIpAddress());
|
||||
sb.append(", resourcePath=");
|
||||
sb.append(adminEvent.getResourcePath());
|
||||
|
||||
if (adminEvent.getError() != null) {
|
||||
sb.append(", error=");
|
||||
sb.append(adminEvent.getError());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.keycloak.Config;
|
|||
import org.keycloak.events.EventListenerProvider;
|
||||
import org.keycloak.events.EventListenerProviderFactory;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
|
@ -16,10 +17,11 @@ import java.util.Set;
|
|||
public class SysoutEventListenerProviderFactory implements EventListenerProviderFactory {
|
||||
|
||||
private Set<EventType> excludedEvents;
|
||||
private Set<OperationType> excludedAdminOperations;
|
||||
|
||||
@Override
|
||||
public EventListenerProvider create(KeycloakSession session) {
|
||||
return new SysoutEventListenerProvider(excludedEvents);
|
||||
return new SysoutEventListenerProvider(excludedEvents, excludedAdminOperations);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -31,6 +33,14 @@ public class SysoutEventListenerProviderFactory implements EventListenerProvider
|
|||
excludedEvents.add(EventType.valueOf(e));
|
||||
}
|
||||
}
|
||||
|
||||
String[] excludesOperations = config.getArray("excludesOperations");
|
||||
if (excludesOperations != null) {
|
||||
excludedAdminOperations = new HashSet<>();
|
||||
for (String e : excludesOperations) {
|
||||
excludedAdminOperations.add(OperationType.valueOf(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
package org.keycloak.examples.providers.events;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.admin.AdminEventQuery;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:giriraj.sharma27@gmail.com">Giriraj Sharma</a>
|
||||
*/
|
||||
public class MemAdminEventQuery implements AdminEventQuery {
|
||||
|
||||
private List<AdminEvent> adminEvents;
|
||||
|
||||
private int first;
|
||||
private int max;
|
||||
|
||||
public MemAdminEventQuery(List<AdminEvent> events) {
|
||||
this.adminEvents = events;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery operation(OperationType... operations) {
|
||||
Iterator<AdminEvent> itr = this.adminEvents.iterator();
|
||||
while (itr.hasNext()) {
|
||||
AdminEvent next = itr.next();
|
||||
boolean include = false;
|
||||
for (OperationType e : operations) {
|
||||
if (next.getOperationType().equals(e)) {
|
||||
include = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!include) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery authRealm(String realmId) {
|
||||
Iterator<AdminEvent> itr = adminEvents.iterator();
|
||||
while (itr.hasNext()) {
|
||||
if (!itr.next().getAuthDetails().getRealmId().equals(realmId)) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery authClient(String clientId) {
|
||||
Iterator<AdminEvent> itr = adminEvents.iterator();
|
||||
while (itr.hasNext()) {
|
||||
if (!itr.next().getAuthDetails().getClientId().equals(clientId)) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery authUser(String userId) {
|
||||
Iterator<AdminEvent> itr = adminEvents.iterator();
|
||||
while (itr.hasNext()) {
|
||||
if (!itr.next().getAuthDetails().getUserId().equals(userId)) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery authIpAddress(String ipAddress) {
|
||||
Iterator<AdminEvent> itr = adminEvents.iterator();
|
||||
while (itr.hasNext()) {
|
||||
if (!itr.next().getAuthDetails().getIpAddress().equals(ipAddress)) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery resourcePath(String resourcePath) {
|
||||
Iterator<AdminEvent> itr = this.adminEvents.iterator();
|
||||
while (itr.hasNext()) {
|
||||
if(!Pattern.compile(resourcePath).matcher(itr.next().getResourcePath()).find()) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
return (AdminEventQuery) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery fromTime(String fromTime) {
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Long from = null;
|
||||
try {
|
||||
from = df.parse(fromTime).getTime();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Iterator<AdminEvent> itr = this.adminEvents.iterator();
|
||||
while (itr.hasNext()) {
|
||||
if (!(itr.next().getTime() >= from)) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery toTime(String toTime) {
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Long to = null;
|
||||
try {
|
||||
to = df.parse(toTime).getTime();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Iterator<AdminEvent> itr = this.adminEvents.iterator();
|
||||
while (itr.hasNext()) {
|
||||
if (!(itr.next().getTime() <= to)) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery firstResult(int result) {
|
||||
this.first = result;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery maxResults(int results) {
|
||||
this.max = results;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AdminEvent> getResultList() {
|
||||
if (adminEvents.size() < first) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
int end = first + max <= adminEvents.size() ? first + max : adminEvents.size();
|
||||
|
||||
return adminEvents.subList(first, end);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
package org.keycloak.examples.providers.events;
|
||||
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.admin.AdminEventQuery;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventQuery;
|
||||
import org.keycloak.events.EventStoreProvider;
|
||||
|
@ -16,10 +19,16 @@ import java.util.Set;
|
|||
public class MemEventStoreProvider implements EventStoreProvider {
|
||||
private final List<Event> events;
|
||||
private final Set<EventType> excludedEvents;
|
||||
private final List<AdminEvent> adminEvents;
|
||||
private final Set<OperationType> excludedOperations;
|
||||
|
||||
public MemEventStoreProvider(List<Event> events, Set<EventType> excludedEvents) {
|
||||
public MemEventStoreProvider(List<Event> events, Set<EventType> excludedEvents,
|
||||
List<AdminEvent> adminEvents, Set<OperationType> excludedOperations) {
|
||||
this.events = events;
|
||||
this.excludedEvents = excludedEvents;
|
||||
|
||||
this.adminEvents = adminEvents;
|
||||
this.excludedOperations = excludedOperations;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -64,6 +73,48 @@ public class MemEventStoreProvider implements EventStoreProvider {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminEventQuery createAdminQuery() {
|
||||
return new MemAdminEventQuery(new LinkedList<>(adminEvents));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAdmin() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAdmin(String realmId) {
|
||||
synchronized(adminEvents) {
|
||||
Iterator<AdminEvent> itr = adminEvents.iterator();
|
||||
while (itr.hasNext()) {
|
||||
if (itr.next().getAuthDetails().getRealmId().equals(realmId)) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAdmin(String realmId, long olderThan) {
|
||||
synchronized(adminEvents) {
|
||||
Iterator<AdminEvent> itr = adminEvents.iterator();
|
||||
while (itr.hasNext()) {
|
||||
AdminEvent e = itr.next();
|
||||
if (e.getAuthDetails().getRealmId().equals(realmId) && e.getTime() < olderThan) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(AdminEvent adminEvent, boolean includeRepresentation) {
|
||||
if (excludedOperations == null || !excludedOperations.contains(adminEvent.getOperationType())) {
|
||||
adminEvents.add(0, adminEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import org.keycloak.events.Event;
|
|||
import org.keycloak.events.EventStoreProvider;
|
||||
import org.keycloak.events.EventStoreProviderFactory;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
|
@ -20,12 +22,13 @@ import java.util.Set;
|
|||
public class MemEventStoreProviderFactory implements EventStoreProviderFactory {
|
||||
|
||||
private List<Event> events;
|
||||
|
||||
private Set<EventType> excludedEvents;
|
||||
private List<AdminEvent> adminEvents;
|
||||
private Set<OperationType> excludedOperations;
|
||||
|
||||
@Override
|
||||
public EventStoreProvider create(KeycloakSession session) {
|
||||
return new MemEventStoreProvider(events, excludedEvents);
|
||||
return new MemEventStoreProvider(events, excludedEvents, adminEvents, excludedOperations);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -39,6 +42,14 @@ public class MemEventStoreProviderFactory implements EventStoreProviderFactory {
|
|||
excludedEvents.add(EventType.valueOf(e));
|
||||
}
|
||||
}
|
||||
|
||||
String excludesOperations = config.get("excludesOperations");
|
||||
if (excludesOperations != null) {
|
||||
excludedOperations = new HashSet<>();
|
||||
for (String e : excludesOperations.split(",")) {
|
||||
excludedOperations.add(OperationType.valueOf(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -49,6 +60,8 @@ public class MemEventStoreProviderFactory implements EventStoreProviderFactory {
|
|||
public void close() {
|
||||
events = null;
|
||||
excludedEvents = null;
|
||||
adminEvents = null;
|
||||
excludedOperations = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -303,6 +303,18 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
controller : 'RealmEventsCtrl'
|
||||
})
|
||||
.when('/realms/:realm/admin-events', {
|
||||
templateUrl : resourceUrl + '/partials/realm-events-admin.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
serverInfo : function(ServerInfoLoader) {
|
||||
return ServerInfoLoader();
|
||||
}
|
||||
},
|
||||
controller : 'RealmAdminEventsCtrl'
|
||||
})
|
||||
.when('/realms/:realm/events-settings', {
|
||||
templateUrl : resourceUrl + '/partials/realm-events-config.html',
|
||||
resolve : {
|
||||
|
|
|
@ -1214,7 +1214,7 @@ module.controller('RealmSMTPSettingsCtrl', function($scope, Current, Realm, real
|
|||
}
|
||||
});
|
||||
|
||||
module.controller('RealmEventsConfigCtrl', function($scope, eventsConfig, RealmEventsConfig, RealmEvents, realm, serverInfo, $location, Notifications, TimeUnit, Dialog) {
|
||||
module.controller('RealmEventsConfigCtrl', function($scope, eventsConfig, RealmEventsConfig, RealmEvents, RealmAdminEvents, realm, serverInfo, $location, Notifications, TimeUnit, Dialog) {
|
||||
$scope.realm = realm;
|
||||
|
||||
$scope.eventsConfig = eventsConfig;
|
||||
|
@ -1232,7 +1232,13 @@ module.controller('RealmEventsConfigCtrl', function($scope, eventsConfig, RealmE
|
|||
$scope.eventSelectOptions = {
|
||||
'multiple': true,
|
||||
'simple_tags': true,
|
||||
'tags': serverInfo.eventTypes
|
||||
'tags': serverInfo.enums['eventType']
|
||||
};
|
||||
|
||||
$scope.adminEnabledEventOperationsOptions = {
|
||||
'multiple': true,
|
||||
'simple_tags': true,
|
||||
'tags': serverInfo.enums['operationType']
|
||||
};
|
||||
|
||||
var oldCopy = angular.copy($scope.eventsConfig);
|
||||
|
@ -1272,6 +1278,14 @@ module.controller('RealmEventsConfigCtrl', function($scope, eventsConfig, RealmE
|
|||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.clearAdminEvents = function() {
|
||||
Dialog.confirmDelete($scope.realm.realm, 'admin-events', function() {
|
||||
RealmAdminEvents.remove({ id : $scope.realm.realm }, function() {
|
||||
Notifications.success("The admin events has been cleared.");
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('RealmEventsCtrl', function($scope, RealmEvents, realm, serverInfo) {
|
||||
|
@ -1281,7 +1295,7 @@ module.controller('RealmEventsCtrl', function($scope, RealmEvents, realm, server
|
|||
$scope.eventSelectOptions = {
|
||||
'multiple': true,
|
||||
'simple_tags': true,
|
||||
'tags': serverInfo.eventTypes
|
||||
'tags': serverInfo.enums['eventType']
|
||||
};
|
||||
|
||||
$scope.query = {
|
||||
|
@ -1342,6 +1356,106 @@ module.controller('RealmEventsCtrl', function($scope, RealmEvents, realm, server
|
|||
$scope.update();
|
||||
});
|
||||
|
||||
module.controller('RealmAdminEventsCtrl', function($scope, RealmAdminEvents, realm, serverInfo, $modal, $filter) {
|
||||
$scope.realm = realm;
|
||||
$scope.page = 0;
|
||||
|
||||
$scope.query = {
|
||||
id : realm.realm,
|
||||
max : 5,
|
||||
first : 0
|
||||
}
|
||||
$scope.query.authRealm = 'master';
|
||||
|
||||
$scope.adminEnabledEventOperationsOptions = {
|
||||
'multiple': true,
|
||||
'simple_tags': true,
|
||||
'tags': serverInfo.enums['operationType']
|
||||
};
|
||||
|
||||
$scope.update = function() {
|
||||
$scope.query.first = 0;
|
||||
for (var i in $scope.query) {
|
||||
if ($scope.query[i] === '') {
|
||||
delete $scope.query[i];
|
||||
}
|
||||
}
|
||||
$scope.events = RealmAdminEvents.query($scope.query);
|
||||
}
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.query.first = 0;
|
||||
$scope.query.max = 5;
|
||||
$scope.query.operationTypes = '';
|
||||
$scope.query.resourcePath = '';
|
||||
$scope.query.authRealm = 'master';
|
||||
$scope.query.authClient = '';
|
||||
$scope.query.authUser = '';
|
||||
$scope.query.authIpAddress = '';
|
||||
$scope.query.dateFrom = '';
|
||||
$scope.query.dateTo = '';
|
||||
|
||||
$scope.update();
|
||||
}
|
||||
|
||||
$scope.queryUpdate = function() {
|
||||
for (var i in $scope.query) {
|
||||
if ($scope.query[i] === '') {
|
||||
delete $scope.query[i];
|
||||
}
|
||||
}
|
||||
$scope.events = RealmAdminEvents.query($scope.query);
|
||||
}
|
||||
|
||||
$scope.firstPage = function() {
|
||||
$scope.query.first = 0;
|
||||
$scope.queryUpdate();
|
||||
}
|
||||
|
||||
$scope.previousPage = function() {
|
||||
$scope.query.first -= parseInt($scope.query.max);
|
||||
if ($scope.query.first < 0) {
|
||||
$scope.query.first = 0;
|
||||
}
|
||||
$scope.queryUpdate();
|
||||
}
|
||||
|
||||
$scope.nextPage = function() {
|
||||
$scope.query.first += parseInt($scope.query.max);
|
||||
$scope.queryUpdate();
|
||||
}
|
||||
|
||||
$scope.update();
|
||||
|
||||
$scope.viewRepresentation = function(event) {
|
||||
$modal.open({
|
||||
templateUrl: resourceUrl + '/partials/modal/realm-events-admin-representation.html',
|
||||
controller: 'RealmAdminEventsModalCtrl',
|
||||
resolve: {
|
||||
event: function () {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$scope.viewAuth = function(event) {
|
||||
$modal.open({
|
||||
templateUrl: resourceUrl + '/partials/modal/realm-events-admin-auth.html',
|
||||
controller: 'RealmAdminEventsModalCtrl',
|
||||
resolve: {
|
||||
event: function () {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
module.controller('RealmAdminEventsModalCtrl', function($scope, $filter, event) {
|
||||
$scope.event = event;
|
||||
});
|
||||
|
||||
module.controller('RealmBruteForceCtrl', function($scope, Realm, realm, $http, $location, Dialog, Notifications, TimeUnit) {
|
||||
console.log('RealmBruteForceCtrl');
|
||||
|
||||
|
|
|
@ -180,6 +180,12 @@ module.factory('RealmEvents', function($resource) {
|
|||
});
|
||||
});
|
||||
|
||||
module.factory('RealmAdminEvents', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:id/admin-events', {
|
||||
id : '@realm'
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('RealmLDAPConnectionTester', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/testLDAPConnection');
|
||||
});
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<div style="padding: 20px 20px 0 20px">
|
||||
<table class="table table-striped table-bordered">
|
||||
<tr><td width="100px">Realm</td><td>{{event.authDetails.realmId}}</td></tr>
|
||||
<tr><td width="100px">Client</td><td>{{event.authDetails.clientId}}</td></tr>
|
||||
<tr><td width="100px">User</td><td>{{event.authDetails.userId}}</td></tr>
|
||||
<tr><td width="100px">IP Address</td><td>{{event.authDetails.ipAddress}}</td></tr>
|
||||
</table>
|
||||
</div>
|
|
@ -0,0 +1,3 @@
|
|||
<div style="padding: 20px 20px 10px 20px">
|
||||
<pre ng-bind = "{{event.representation}} | json"></pre>
|
||||
</div>
|
|
@ -0,0 +1,128 @@
|
|||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||
<h1>
|
||||
<span><strong>Admin Events</strong> {{realm.realm|capitalize}}</span>
|
||||
<kc-tooltip>Displays saved admin events for the realm. Events are related to admin account, for example a realm creation. To enable persisted events go to config.</kc-tooltip>
|
||||
</h1>
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li data-ng-class="(path[2] == 'events') && 'active'"><a href="#/realms/{{realm.realm}}/events">Login Events</a></li>
|
||||
<li data-ng-class="(path[2] == 'admin-events') && 'active'"><a href="#/realms/{{realm.realm}}/admin-events">Admin Events</a></li>
|
||||
<li data-ng-class="(path[2] == 'events-settings') && 'active'"><a href="#/realms/{{realm.realm}}/events-settings">Config</a></li>
|
||||
</ul>
|
||||
<h2></h2>
|
||||
|
||||
<div id="content">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="kc-table-actions" colspan="5">
|
||||
<div class="pull-right">
|
||||
<select data-ng-model="query.max" data-ng-click="update()" class="btn btn-default">
|
||||
<option>5</option>
|
||||
<option>10</option>
|
||||
<option>50</option>
|
||||
<option>100</option>
|
||||
</select>
|
||||
<button class="btn btn-default" data-ng-click="filter = !filter">
|
||||
<span class="glyphicon glyphicon-plus" data-ng-show="!filter"></span>
|
||||
<span class="glyphicon glyphicon-minus" data-ng-show="filter"></span>
|
||||
Filter
|
||||
</button>
|
||||
<button class="btn btn-default btn-primary" data-ng-click="update()">Update</button>
|
||||
<button class="btn btn-default btn-primary" data-ng-click="reset()">Reset</button>
|
||||
</div>
|
||||
<form class="form-horizontal" data-ng-show="filter">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="adminEnabledEventOperations">Operation Types</label>
|
||||
<div class="col-sm-5">
|
||||
<input ui-select2="adminEnabledEventOperationsOptions" id="adminEnabledEventOperations" ng-model="query.operationTypes" data-placeholder="Select operations..."/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="resource">Resource Path</label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="text" id="resource" name="resource" data-ng-model="query.resourcePath">
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Filter by resource path. Supports wildcards '*' to match a single part of the path and '**' matches multiple parts. For example 'realms/*/clients/asbc' matches client with id asbc in any realm, while or 'realms/master/**' matches anything in the master realm." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="dateFrom">Date (From)</label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="date" id="dateFrom" name="dateFrom" data-ng-model="query.dateFrom">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="dateTo">Date (To)</label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="date" id="dateTo" name="dateTo" data-ng-model="query.dateTo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<fieldset>
|
||||
<legend><span class="text">Authentication Details</span></legend>
|
||||
|
||||
<div class="form-group" data-ng-show="'master' === realm.realm">
|
||||
<label class="col-sm-2 control-label" for="realm">Realm</label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="text" id="realm" name="realm" data-ng-model="query.authRealm">
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Filter by realm Id. This filter is supported only for master realm." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="client">Client</label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="text" id="client" name="client" data-ng-model="query.authClient">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="user">User</label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="text" id="user" name="user" data-ng-model="query.authUser">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="ipAddress">IP Address</label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="text" id="ipAddress" name="ipAddress" data-ng-model="query.authIpAddress">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
</form>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th width="100px">Time</th>
|
||||
<th width="180px">Operation Type</th>
|
||||
<th width="180px">Resource Path</th>
|
||||
<th>Details</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="7">
|
||||
<button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0"><i data-ng-class="query.first == 0 && 'text-muted'" class="fa fa-angle-double-left"></i></button>
|
||||
<button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0"><i data-ng-class="query.first == 0 && 'text-muted'" class="fa fa-angle-left"></i></button>
|
||||
<button data-ng-click="nextPage()" class="next" ng-disabled="events.length < query.max"><i data-ng-class="events.length < query.max && 'text-muted'" class="fa fa-angle-right"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
<tr data-ng-repeat="event in events">
|
||||
<td>{{event.time|date:'shortDate'}}<br>{{event.time|date:'mediumTime'}}</td>
|
||||
<td data-ng-class="events-error">{{event.operationType}}</td>
|
||||
<td>{{event.resourcePath}}</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-default btn-xs" data-ng-click="viewAuth(event)">
|
||||
Auth
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-xs" data-ng-click="viewRepresentation(event)" data-ng-show="event.representation">
|
||||
Representation
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<kc-menu></kc-menu>
|
|
@ -2,69 +2,120 @@
|
|||
<h1><strong>Events</strong> {{realm.realm|capitalize}}</span> Events</h1>
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li data-ng-class="(path[2] == 'events') && 'active'"><a href="#/realms/{{realm.realm}}/events">View</a></li>
|
||||
<li data-ng-class="(path[2] == 'events') && 'active'"><a href="#/realms/{{realm.realm}}/events">Login Events</a></li>
|
||||
<li data-ng-class="(path[2] == 'admin-events') && 'active'"><a href="#/realms/{{realm.realm}}/admin-events">Admin Events</a></li>
|
||||
<li data-ng-class="(path[2] == 'events-settings') && 'active'"><a href="#/realms/{{realm.realm}}/events-settings">Config</a></li>
|
||||
</ul>
|
||||
<div id="content">
|
||||
<h2><span>{{realm.realm}}</span> Events Config</h2>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageEvents">
|
||||
<fieldset class="border-top">
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="enabled">Save Events</label>
|
||||
<div class="col-md-6">
|
||||
<input ng-model="eventsConfig.eventsEnabled" name="enabled" id="enabled" onoffswitch />
|
||||
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageEvents">
|
||||
|
||||
<fieldset class="border-top">
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="eventsListeners" class="control-label">Event Listeners</label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<select ui-select2 ng-model="eventsConfig.eventsListeners" data-placeholder="Select an action..." multiple>
|
||||
<option ng-repeat="listener in eventListeners" value="{{listener}}">{{listener}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<span tooltip-placement="right" tooltip="Configure what listeners receive events for the realm." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<kc-tooltip>If enabled events are saved to the database which makes events available to the admin and account management consoles.</kc-tooltip>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-group" data-ng-show="eventsConfig.eventsEnabled">
|
||||
<label class="col-md-2 control-label" for="enabledEventTypes" class="control-label">Saved Types</label>
|
||||
<fieldset>
|
||||
<legend><span class="text">Login Events Settings</span></legend>
|
||||
|
||||
<div class="col-md-6">
|
||||
<input ui-select2="eventSelectOptions" id="enabledEventTypes" ng-model="eventsConfig.enabledEventTypes" data-placeholder="Select event types..."/>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="enabled">Save Events</label>
|
||||
<div class="col-md-6">
|
||||
<input ng-model="eventsConfig.eventsEnabled" name="enabled" id="enabled" onoffswitch />
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="If enabled login events are saved to the database which makes events available to the admin and account management consoles." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
|
||||
<kc-tooltip>Configure what event types are saved. By default events related to login and users modifying their accounts are persisted.</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group" data-ng-show="eventsConfig.eventsEnabled">
|
||||
<label class="col-md-2 control-label" for="enabledEventTypes" class="control-label">Saved Types</label>
|
||||
|
||||
<div class="form-group" data-ng-show="access.manageEvents && eventsConfig.eventsEnabled">
|
||||
<label class="col-md-2 control-label" for="password">Clear Events</label>
|
||||
<div class="col-md-6">
|
||||
<button class="btn btn-danger" type="submit" data-ng-click="clearEvents()" >Clear Events</button>
|
||||
</div>
|
||||
<kc-tooltip>Deletes all events in the database.</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group input-select" data-ng-show="eventsConfig.eventsEnabled">
|
||||
<label class="col-md-2 control-label" for="expiration">Expiration</label>
|
||||
<div class="col-md-6 form-inline">
|
||||
<input class="form-control" type="number" data-ng-model="eventsConfig.eventsExpiration" id="expiration" name="expiration" min="0"/>
|
||||
<select class="form-control" name="expirationUnit" data-ng-model="eventsConfig.expirationUnit" >
|
||||
<option>Minutes</option>
|
||||
<option>Hours</option>
|
||||
<option>Days</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-1"></div>
|
||||
<kc-tooltip>Sets the expiration for events. Expired events are periodically deleted from the database.</kc-tooltip>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<input ui-select2="eventSelectOptions" id="enabledEventTypes" ng-model="eventsConfig.enabledEventTypes" data-placeholder="Select event types..."/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="eventsListeners" class="control-label">Listeners</label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<select ui-select2 ng-model="eventsConfig.eventsListeners" data-placeholder="Select an action..." multiple>
|
||||
<option ng-repeat="listener in eventListeners" value="{{listener}}">{{listener}}</option>
|
||||
</select>
|
||||
<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>
|
||||
|
||||
<kc-tooltip>Configure what listeners receive events for the realm.</kc-tooltip>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="form-group" data-ng-show="access.manageEvents && eventsConfig.eventsEnabled">
|
||||
<label class="col-md-2 control-label" for="password">Clear Events</label>
|
||||
<div class="col-md-6">
|
||||
<button class="btn btn-danger" type="submit" data-ng-click="clearEvents()" >Clear Events</button>
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Deletes all events in the database." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group input-select" data-ng-show="eventsConfig.eventsEnabled">
|
||||
<label class="col-md-2 control-label" for="expiration">Expiration</label>
|
||||
<div class="col-md-6">
|
||||
<input class="form-control" type="number" data-ng-model="eventsConfig.eventsExpiration" id="expiration" name="expiration" min="0"/>
|
||||
</div>
|
||||
<div class="col-md-2 select-kc">
|
||||
<select name="expirationUnit" data-ng-model="eventsConfig.expirationUnit" >
|
||||
<option>Minutes</option>
|
||||
<option>Hours</option>
|
||||
<option>Days</option>
|
||||
</select>
|
||||
<span tooltip-placement="right" tooltip="Sets the expiration for events. Expired events are periodically deleted from the database." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="pull-right form-actions" data-ng-show="access.manageEvents">
|
||||
<button data-kc-reset data-ng-show="changed">Clear changes</button>
|
||||
<button data-kc-save data-ng-show="changed">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<fieldset>
|
||||
<legend><span class="text">Admin Events Settings</span></legend>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="adminEventsEnabled">Save Events</label>
|
||||
<div class="col-md-6">
|
||||
<input ng-model="eventsConfig.adminEventsEnabled" name="adminEventsEnabled" id="adminEventsEnabled" onoffswitch />
|
||||
</div>
|
||||
|
||||
<span tooltip-placement="right" tooltip="If enabled admin events are saved to the database which makes events available to the admin console." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group" data-ng-show="eventsConfig.adminEventsEnabled">
|
||||
<label class="col-md-2 control-label" for="adminEnabledEventOperations" class="control-label">Saved Operations</label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<input ui-select2="adminEnabledEventOperationsOptions" id="adminEnabledEventOperations" ng-model="eventsConfig.adminEnabledEventOperations" data-placeholder="Select operations..."/>
|
||||
</div>
|
||||
|
||||
<span tooltip-placement="right" tooltip="Configure what operations are saved." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group" data-ng-show="eventsConfig.adminEventsEnabled">
|
||||
<label class="col-md-2 control-label" for="adminEventsDetailsEnabled">Include Representation</label>
|
||||
<div class="col-md-6">
|
||||
<input ng-model="eventsConfig.adminEventsDetailsEnabled" name="adminEventsDetailsEnabled" id="adminEventsDetailsEnabled" onoffswitch />
|
||||
</div>
|
||||
|
||||
<span tooltip-placement="right" tooltip="Include JSON representation for create and update requests." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group" data-ng-show="access.manageEvents && eventsConfig.adminEventsEnabled">
|
||||
<label class="col-md-2 control-label" for="password">Clear Admin Events</label>
|
||||
<div class="col-md-6">
|
||||
<button class="btn btn-danger" type="submit" data-ng-click="clearAdminEvents()" >Clear Admin Events</button>
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Deletes all admin events in the database." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
|
||||
<div class="pull-right form-actions" data-ng-show="access.manageEvents">
|
||||
<button data-kc-reset data-ng-show="changed">Clear changes</button>
|
||||
<button data-kc-save data-ng-show="changed">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<kc-menu></kc-menu>
|
||||
<kc-menu></kc-menu>
|
|
@ -5,7 +5,8 @@
|
|||
</h1>
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li data-ng-class="(path[2] == 'events') && 'active'"><a href="#/realms/{{realm.realm}}/events">View</a></li>
|
||||
<li data-ng-class="(path[2] == 'events') && 'active'"><a href="#/realms/{{realm.realm}}/events">Login Events</a></li>
|
||||
<li data-ng-class="(path[2] == 'admin-events') && 'active'"><a href="#/realms/{{realm.realm}}/admin-events">Admin Events</a></li>
|
||||
<li data-ng-class="(path[2] == 'events-settings') && 'active'"><a href="#/realms/{{realm.realm}}/events-settings">Config</a></li>
|
||||
</ul>
|
||||
|
||||
|
@ -121,4 +122,4 @@
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<kc-menu></kc-menu>
|
||||
<kc-menu></kc-menu>
|
|
@ -0,0 +1,37 @@
|
|||
package org.keycloak.email.freemarker.beans;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:giriraj.sharma27@gmail.com">Giriraj Sharma</a>
|
||||
*/
|
||||
public class AdminEventBean {
|
||||
|
||||
private AdminEvent adminEvent;
|
||||
|
||||
public AdminEventBean(AdminEvent adminEvent) {
|
||||
this.adminEvent = adminEvent;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return new Date(adminEvent.getTime());
|
||||
}
|
||||
|
||||
public String getOperationType() {
|
||||
return adminEvent.getOperationType().toString().toLowerCase();
|
||||
}
|
||||
|
||||
public String getClient() {
|
||||
return adminEvent.getAuthDetails().getClientId();
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return adminEvent.getAuthDetails().getIpAddress();
|
||||
}
|
||||
|
||||
public String getResourcePath() {
|
||||
return adminEvent.getResourcePath();
|
||||
}
|
||||
}
|
|
@ -232,7 +232,19 @@ public interface RealmModel extends RoleContainerModel {
|
|||
Set<String> getEnabledEventTypes();
|
||||
|
||||
void setEnabledEventTypes(Set<String> enabledEventTypes);
|
||||
|
||||
boolean isAdminEventsEnabled();
|
||||
|
||||
void setAdminEventsEnabled(boolean enabled);
|
||||
|
||||
Set<String> getAdminEnabledEventOperations();
|
||||
|
||||
void setAdminEnabledEventOperations(Set<String> adminEnabledEventOperations);
|
||||
|
||||
boolean isAdminEventsDetailsEnabled();
|
||||
|
||||
void setAdminEventsDetailsEnabled(boolean enabled);
|
||||
|
||||
ClientModel getMasterAdminClient();
|
||||
|
||||
void setMasterAdminClient(ClientModel client);
|
||||
|
|
|
@ -63,7 +63,11 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
|||
private long eventsExpiration;
|
||||
private List<String> eventsListeners = new ArrayList<String>();
|
||||
private List<String> enabledEventTypes = new ArrayList<String>();
|
||||
|
||||
|
||||
protected boolean adminEventsEnabled;
|
||||
protected List<String> adminEnabledEventOperations = new ArrayList<String>();;
|
||||
protected boolean adminEventsDetailsEnabled;
|
||||
|
||||
private String masterAdminClient;
|
||||
|
||||
private boolean internationalizationEnabled;
|
||||
|
@ -391,6 +395,30 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
|||
this.enabledEventTypes = enabledEventTypes;
|
||||
}
|
||||
|
||||
public boolean isAdminEventsEnabled() {
|
||||
return adminEventsEnabled;
|
||||
}
|
||||
|
||||
public void setAdminEventsEnabled(boolean adminEventsEnabled) {
|
||||
this.adminEventsEnabled = adminEventsEnabled;
|
||||
}
|
||||
|
||||
public List<String> getAdminEnabledEventOperations() {
|
||||
return adminEnabledEventOperations;
|
||||
}
|
||||
|
||||
public void setAdminEnabledEventOperations(List<String> adminEnabledEventOperations) {
|
||||
this.adminEnabledEventOperations = adminEnabledEventOperations;
|
||||
}
|
||||
|
||||
public boolean isAdminEventsDetailsEnabled() {
|
||||
return adminEventsDetailsEnabled;
|
||||
}
|
||||
|
||||
public void setAdminEventsDetailsEnabled(boolean adminEventsDetailsEnabled) {
|
||||
this.adminEventsDetailsEnabled = adminEventsDetailsEnabled;
|
||||
}
|
||||
|
||||
public String getMasterAdminClient() {
|
||||
return masterAdminClient;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.keycloak.models.UserCredentialModel;
|
|||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||
|
@ -192,6 +193,14 @@ public class ModelToRepresentation {
|
|||
rep.setEnabledEventTypes(new LinkedList<String>(realm.getEnabledEventTypes()));
|
||||
}
|
||||
|
||||
rep.setAdminEventsEnabled(realm.isAdminEventsEnabled());
|
||||
|
||||
if(realm.getAdminEnabledEventOperations() != null) {
|
||||
rep.setAdminEnabledEventOperations(new LinkedList<String>(realm.getAdminEnabledEventOperations()));
|
||||
}
|
||||
|
||||
rep.setAdminEventsDetailsEnabled(realm.isAdminEventsDetailsEnabled());
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
|
|
|
@ -410,10 +410,16 @@ public class RepresentationToModel {
|
|||
if (rep.getAccountTheme() != null) realm.setAccountTheme(rep.getAccountTheme());
|
||||
if (rep.getAdminTheme() != null) realm.setAdminTheme(rep.getAdminTheme());
|
||||
if (rep.getEmailTheme() != null) realm.setEmailTheme(rep.getEmailTheme());
|
||||
|
||||
if (rep.isEventsEnabled() != null) realm.setEventsEnabled(rep.isEventsEnabled());
|
||||
if (rep.getEventsExpiration() != null) realm.setEventsExpiration(rep.getEventsExpiration());
|
||||
if (rep.getEventsListeners() != null) realm.setEventsListeners(new HashSet<>(rep.getEventsListeners()));
|
||||
if (rep.getEnabledEventTypes() != null) realm.setEnabledEventTypes(new HashSet<>(rep.getEnabledEventTypes()));
|
||||
|
||||
if (rep.isAdminEventsEnabled() != null) realm.setAdminEventsEnabled(rep.isAdminEventsEnabled());
|
||||
if (rep.getAdminEnabledEventOperations() != null) realm.setAdminEnabledEventOperations(new HashSet<>(rep.getAdminEnabledEventOperations()));
|
||||
if (rep.isAdminEventsDetailsEnabled() != null) realm.setAdminEventsDetailsEnabled(rep.isAdminEventsDetailsEnabled());
|
||||
|
||||
|
||||
if (rep.getPasswordPolicy() != null) realm.setPasswordPolicy(new PasswordPolicy(rep.getPasswordPolicy()));
|
||||
|
||||
|
|
|
@ -958,6 +958,40 @@ public class RealmAdapter implements RealmModel {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdminEventsEnabled() {
|
||||
return realm.isAdminEventsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdminEventsEnabled(boolean enabled) {
|
||||
realm.setAdminEventsEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAdminEnabledEventOperations() {
|
||||
return new HashSet<String>(realm.getAdminEnabledEventOperations());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdminEnabledEventOperations(Set<String> adminEnabledEventOperations) {
|
||||
if (adminEnabledEventOperations != null) {
|
||||
realm.setAdminEnabledEventOperations(new ArrayList<String>(adminEnabledEventOperations));
|
||||
} else {
|
||||
realm.setAdminEnabledEventOperations(Collections.EMPTY_LIST);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdminEventsDetailsEnabled() {
|
||||
return realm.isAdminEventsDetailsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdminEventsDetailsEnabled(boolean enabled) {
|
||||
realm.setAdminEventsDetailsEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getMasterAdminClient() {
|
||||
return this.masterAdminApp;
|
||||
|
|
|
@ -750,6 +750,42 @@ public class RealmAdapter implements RealmModel {
|
|||
updated.setEnabledEventTypes(enabledEventTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdminEventsEnabled() {
|
||||
if (updated != null) return updated.isAdminEventsEnabled();
|
||||
return cached.isAdminEventsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdminEventsEnabled(boolean enabled) {
|
||||
getDelegateForUpdate();
|
||||
updated.setAdminEventsEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAdminEnabledEventOperations() {
|
||||
if (updated != null) return updated.getAdminEnabledEventOperations();
|
||||
return cached.getAdminEnabledEventOperations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdminEnabledEventOperations(Set<String> adminEnabledEventOperations) {
|
||||
getDelegateForUpdate();
|
||||
updated.setAdminEnabledEventOperations(adminEnabledEventOperations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdminEventsDetailsEnabled() {
|
||||
if (updated != null) return updated.isAdminEventsDetailsEnabled();
|
||||
return cached.isAdminEventsDetailsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdminEventsDetailsEnabled(boolean enabled) {
|
||||
getDelegateForUpdate();
|
||||
updated.setAdminEventsDetailsEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getMasterAdminClient() {
|
||||
return cacheSession.getRealm(Config.getAdminRealm()).getClientById(cached.getMasterAdminClient());
|
||||
|
@ -923,4 +959,5 @@ public class RealmAdapter implements RealmModel {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -79,6 +79,9 @@ public class CachedRealm {
|
|||
private long eventsExpiration;
|
||||
private Set<String> eventsListeners = new HashSet<String>();
|
||||
private Set<String> enabledEventTypes = new HashSet<String>();
|
||||
protected boolean adminEventsEnabled;
|
||||
protected Set<String> adminEnabledEventOperations = new HashSet<String>();
|
||||
protected boolean adminEventsDetailsEnabled;
|
||||
private List<String> defaultRoles = new LinkedList<String>();
|
||||
private Map<String, String> realmRoles = new HashMap<String, String>();
|
||||
private Map<String, String> clients = new HashMap<String, String>();
|
||||
|
@ -153,6 +156,11 @@ public class CachedRealm {
|
|||
eventsExpiration = model.getEventsExpiration();
|
||||
eventsListeners.addAll(model.getEventsListeners());
|
||||
enabledEventTypes.addAll(model.getEnabledEventTypes());
|
||||
|
||||
adminEventsEnabled = model.isAdminEventsEnabled();
|
||||
adminEnabledEventOperations.addAll(model.getAdminEnabledEventOperations());
|
||||
adminEventsDetailsEnabled = model.isAdminEventsDetailsEnabled();
|
||||
|
||||
defaultRoles.addAll(model.getDefaultRoles());
|
||||
masterAdminClient = model.getMasterAdminClient().getId();
|
||||
|
||||
|
@ -350,6 +358,18 @@ public class CachedRealm {
|
|||
return enabledEventTypes;
|
||||
}
|
||||
|
||||
public boolean isAdminEventsEnabled() {
|
||||
return adminEventsEnabled;
|
||||
}
|
||||
|
||||
public Set<String> getAdminEnabledEventOperations() {
|
||||
return adminEnabledEventOperations;
|
||||
}
|
||||
|
||||
public boolean isAdminEventsDetailsEnabled() {
|
||||
return adminEventsDetailsEnabled;
|
||||
}
|
||||
|
||||
public List<UserFederationProviderModel> getUserFederationProviders() {
|
||||
return userFederationProviders;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
|||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
|
@ -1065,7 +1066,41 @@ public class RealmAdapter implements RealmModel {
|
|||
realm.setEnabledEventTypes(enabledEventTypes);
|
||||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdminEventsEnabled() {
|
||||
return realm.isAdminEventsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdminEventsEnabled(boolean enabled) {
|
||||
realm.setAdminEventsEnabled(enabled);
|
||||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAdminEnabledEventOperations() {
|
||||
return realm.getAdminEnabledEventOperations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdminEnabledEventOperations(Set<String> adminEnabledEventOperations) {
|
||||
realm.setAdminEnabledEventOperations(adminEnabledEventOperations);
|
||||
em.flush();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdminEventsDetailsEnabled() {
|
||||
return realm.isAdminEventsDetailsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdminEventsDetailsEnabled(boolean enabled) {
|
||||
realm.setAdminEventsDetailsEnabled(enabled);
|
||||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getMasterAdminClient() {
|
||||
return new ClientAdapter(this, em, session, realm.getMasterAdminClient());
|
||||
|
@ -1327,4 +1362,5 @@ public class RealmAdapter implements RealmModel {
|
|||
mapping.setConfig(config);
|
||||
return mapping;
|
||||
}
|
||||
|
||||
}
|
|
@ -134,7 +134,18 @@ public class RealmEntity {
|
|||
@Column(name="VALUE")
|
||||
@CollectionTable(name="REALM_ENABLED_EVENT_TYPES", joinColumns={ @JoinColumn(name="REALM_ID") })
|
||||
protected Set<String> enabledEventTypes = new HashSet<String>();
|
||||
|
||||
|
||||
@Column(name="ADMIN_EVENTS_ENABLED")
|
||||
protected boolean adminEventsEnabled;
|
||||
|
||||
@ElementCollection
|
||||
@Column(name="VALUE")
|
||||
@CollectionTable(name="REALM_ENABLED_ADMIN_EVENT_OPERATIONS", joinColumns={ @JoinColumn(name="REALM_ID") })
|
||||
protected Set<String> adminEnabledEventOperations = new HashSet<String>();
|
||||
|
||||
@Column(name="ADMIN_EVENTS_DETAILS_ENABLED")
|
||||
protected boolean adminEventsDetailsEnabled;
|
||||
|
||||
@OneToOne
|
||||
@JoinColumn(name="MASTER_ADMIN_CLIENT")
|
||||
protected ClientEntity masterAdminClient;
|
||||
|
@ -437,6 +448,30 @@ public class RealmEntity {
|
|||
this.enabledEventTypes = enabledEventTypes;
|
||||
}
|
||||
|
||||
public boolean isAdminEventsEnabled() {
|
||||
return adminEventsEnabled;
|
||||
}
|
||||
|
||||
public void setAdminEventsEnabled(boolean adminEventsEnabled) {
|
||||
this.adminEventsEnabled = adminEventsEnabled;
|
||||
}
|
||||
|
||||
public Set<String> getAdminEnabledEventOperations() {
|
||||
return adminEnabledEventOperations;
|
||||
}
|
||||
|
||||
public void setAdminEnabledEventOperations(Set<String> adminEnabledEventOperations) {
|
||||
this.adminEnabledEventOperations = adminEnabledEventOperations;
|
||||
}
|
||||
|
||||
public boolean isAdminEventsDetailsEnabled() {
|
||||
return adminEventsDetailsEnabled;
|
||||
}
|
||||
|
||||
public void setAdminEventsDetailsEnabled(boolean adminEventsDetailsEnabled) {
|
||||
this.adminEventsDetailsEnabled = adminEventsDetailsEnabled;
|
||||
}
|
||||
|
||||
public ClientEntity getMasterAdminClient() {
|
||||
return masterAdminClient;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.models.mongo.keycloak.adapters;
|
|||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.QueryBuilder;
|
||||
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.enums.SslRequired;
|
||||
import org.keycloak.models.ClientModel;
|
||||
|
@ -985,7 +986,45 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
}
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdminEventsEnabled() {
|
||||
return realm.isAdminEventsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdminEventsEnabled(boolean enabled) {
|
||||
realm.setAdminEventsEnabled(enabled);
|
||||
updateRealm();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAdminEnabledEventOperations() {
|
||||
return new HashSet<String>(realm.getAdminEnabledEventOperations());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdminEnabledEventOperations(Set<String> adminEnabledEventOperations) {
|
||||
if (adminEnabledEventOperations != null) {
|
||||
realm.setAdminEnabledEventOperations(new ArrayList<String>(adminEnabledEventOperations));
|
||||
} else {
|
||||
realm.setAdminEnabledEventOperations(Collections.EMPTY_LIST);
|
||||
}
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdminEventsDetailsEnabled() {
|
||||
return realm.isAdminEventsDetailsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdminEventsDetailsEnabled(boolean enabled) {
|
||||
realm.setAdminEventsDetailsEnabled(enabled);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getMasterAdminClient() {
|
||||
MongoClientEntity appData = getMongoStore().loadEntity(MongoClientEntity.class, realm.getMasterAdminClient(), invocationContext);
|
||||
|
|
|
@ -168,6 +168,12 @@ public class RealmManager {
|
|||
if(rep.getEnabledEventTypes() != null) {
|
||||
realm.setEnabledEventTypes(new HashSet<String>(rep.getEnabledEventTypes()));
|
||||
}
|
||||
|
||||
realm.setAdminEventsEnabled(rep.isAdminEventsEnabled());
|
||||
if(rep.getAdminEnabledEventOperations() != null) {
|
||||
realm.setAdminEnabledEventOperations(new HashSet<String>(rep.getAdminEnabledEventOperations()));
|
||||
}
|
||||
realm.setAdminEventsDetailsEnabled(rep.isAdminEventsDetailsEnabled());
|
||||
}
|
||||
|
||||
// Should be RealmManager moved to model/api instead of referencing methods this way?
|
||||
|
|
|
@ -8,6 +8,7 @@ import org.jboss.resteasy.spi.NotFoundException;
|
|||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.jboss.resteasy.spi.UnauthorizedException;
|
||||
import org.keycloak.ClientConnection;
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.models.AdminRoles;
|
||||
import org.keycloak.models.ClientModel;
|
||||
|
@ -29,6 +30,7 @@ import javax.ws.rs.core.HttpHeaders;
|
|||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
|
@ -185,8 +187,11 @@ public class AdminRoot {
|
|||
}
|
||||
|
||||
Cors.add(request).allowedOrigins(auth.getToken()).allowedMethods("GET", "PUT", "POST", "DELETE").auth().build(response);
|
||||
|
||||
RealmsAdminResource adminResource = new RealmsAdminResource(auth, tokenManager);
|
||||
|
||||
AdminEventBuilder adminEvent = new AdminEventBuilder(auth.getRealm(), session, clientConnection);
|
||||
adminEvent.user(auth.getUser()).client(auth.getClient());
|
||||
|
||||
RealmsAdminResource adminResource = new RealmsAdminResource(auth, tokenManager, adminEvent);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(adminResource);
|
||||
return adminResource;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
|
|||
import org.jboss.resteasy.spi.BadRequestException;
|
||||
import org.jboss.resteasy.spi.NotAcceptableException;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
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.MediaType;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -47,11 +50,12 @@ public class ClientAttributeCertificateResource {
|
|||
private RealmAuth auth;
|
||||
protected ClientModel client;
|
||||
protected KeycloakSession session;
|
||||
protected AdminEventBuilder adminEvent;
|
||||
protected String attributePrefix;
|
||||
protected String privateAttribute;
|
||||
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, AdminEventBuilder adminEvent) {
|
||||
this.realm = realm;
|
||||
this.auth = auth;
|
||||
this.client = client;
|
||||
|
@ -59,6 +63,7 @@ public class ClientAttributeCertificateResource {
|
|||
this.attributePrefix = attributePrefix;
|
||||
this.privateAttribute = attributePrefix + "." + PRIVATE_KEY;
|
||||
this.certificateAttribute = attributePrefix + "." + X509CERTIFICATE;
|
||||
this.adminEvent = adminEvent;
|
||||
}
|
||||
|
||||
public static class ClientKeyPairInfo {
|
||||
|
@ -94,6 +99,7 @@ public class ClientAttributeCertificateResource {
|
|||
ClientKeyPairInfo info = new ClientKeyPairInfo();
|
||||
info.setCertificate(client.getAttribute(certificateAttribute));
|
||||
info.setPrivateKey(client.getAttribute(privateAttribute));
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return info;
|
||||
}
|
||||
|
||||
|
@ -134,6 +140,7 @@ public class ClientAttributeCertificateResource {
|
|||
ClientKeyPairInfo info = new ClientKeyPairInfo();
|
||||
info.setCertificate(client.getAttribute(certificateAttribute));
|
||||
info.setPrivateKey(client.getAttribute(privateAttribute));
|
||||
adminEvent.operation(OperationType.CREATE).resourcePath(session.getContext().getUri().getPath()).representation(info).success();
|
||||
return info;
|
||||
}
|
||||
|
||||
|
@ -190,7 +197,8 @@ public class ClientAttributeCertificateResource {
|
|||
client.setAttribute(certificateAttribute, certPem);
|
||||
info.setCertificate(certPem);
|
||||
}
|
||||
|
||||
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).representation(info).success();
|
||||
return info;
|
||||
}
|
||||
|
||||
|
@ -316,6 +324,9 @@ public class ClientAttributeCertificateResource {
|
|||
stream.flush();
|
||||
stream.close();
|
||||
byte[] rtn = stream.toByteArray();
|
||||
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
|
||||
return rtn;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
|
|
|
@ -5,6 +5,8 @@ import org.jboss.resteasy.annotations.cache.NoCache;
|
|||
import org.jboss.resteasy.spi.BadRequestException;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
|
@ -40,6 +42,7 @@ 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.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
@ -57,6 +60,7 @@ public class ClientResource {
|
|||
protected static final Logger logger = Logger.getLogger(ClientResource.class);
|
||||
protected RealmModel realm;
|
||||
private RealmAuth auth;
|
||||
private AdminEventBuilder adminEvent;
|
||||
protected ClientModel client;
|
||||
protected KeycloakSession session;
|
||||
|
||||
|
@ -70,19 +74,21 @@ public class ClientResource {
|
|||
return keycloak;
|
||||
}
|
||||
|
||||
public ClientResource(RealmModel realm, RealmAuth auth, ClientModel clientModel, KeycloakSession session) {
|
||||
public ClientResource(RealmModel realm, RealmAuth auth, ClientModel clientModel, KeycloakSession session, AdminEventBuilder adminEvent) {
|
||||
this.realm = realm;
|
||||
this.auth = auth;
|
||||
this.client = clientModel;
|
||||
this.session = session;
|
||||
this.adminEvent = adminEvent;
|
||||
|
||||
auth.init(RealmAuth.Resource.CLIENT);
|
||||
}
|
||||
|
||||
@Path("protocol-mappers")
|
||||
public ProtocolMappersResource getProtocolMappers() {
|
||||
ProtocolMappersResource mappers = new ProtocolMappersResource(client, auth);
|
||||
ProtocolMappersResource mappers = new ProtocolMappersResource(client, auth, adminEvent);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(mappers);
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return mappers;
|
||||
}
|
||||
|
||||
|
@ -98,6 +104,7 @@ public class ClientResource {
|
|||
|
||||
try {
|
||||
RepresentationToModel.updateClient(rep, client);
|
||||
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(rep).success();
|
||||
return Response.noContent().build();
|
||||
} catch (ModelDuplicateException e) {
|
||||
return ErrorResponse.exists("Client " + rep.getClientId() + " already exists");
|
||||
|
@ -115,7 +122,7 @@ public class ClientResource {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public ClientRepresentation getClient() {
|
||||
auth.requireView();
|
||||
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return ModelToRepresentation.toRepresentation(client);
|
||||
}
|
||||
|
||||
|
@ -126,7 +133,7 @@ public class ClientResource {
|
|||
*/
|
||||
@Path("certificates/{attr}")
|
||||
public ClientAttributeCertificateResource getCertficateResource(@PathParam("attr") String attributePrefix) {
|
||||
return new ClientAttributeCertificateResource(realm, auth, client, session, attributePrefix);
|
||||
return new ClientAttributeCertificateResource(realm, auth, client, session, attributePrefix, adminEvent);
|
||||
}
|
||||
|
||||
|
||||
|
@ -145,6 +152,8 @@ public class ClientResource {
|
|||
|
||||
ClientManager clientManager = new ClientManager(new RealmManager(session));
|
||||
Object rep = clientManager.toInstallationRepresentation(realm, client, getKeycloakApplication().getBaseUri(uriInfo));
|
||||
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
|
||||
|
||||
// TODO Temporary solution to pretty-print
|
||||
return JsonSerialization.mapper.writerWithDefaultPrettyPrinter().writeValueAsString(rep);
|
||||
|
@ -164,6 +173,9 @@ public class ClientResource {
|
|||
auth.requireView();
|
||||
|
||||
ClientManager clientManager = new ClientManager(new RealmManager(session));
|
||||
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
|
||||
|
||||
return clientManager.toJBossSubsystemConfig(realm, client, getKeycloakApplication().getBaseUri(uriInfo));
|
||||
}
|
||||
|
||||
|
@ -176,6 +188,7 @@ public class ClientResource {
|
|||
public void deleteClient() {
|
||||
auth.requireManage();
|
||||
new ClientManager(new RealmManager(session)).removeClient(realm, client);
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
|
||||
}
|
||||
|
||||
|
||||
|
@ -194,6 +207,7 @@ public class ClientResource {
|
|||
logger.debug("regenerateSecret");
|
||||
UserCredentialModel cred = KeycloakModelUtils.generateSecret(client);
|
||||
CredentialRepresentation rep = ModelToRepresentation.toRepresentation(cred);
|
||||
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getPath()).representation(rep).success();
|
||||
return rep;
|
||||
}
|
||||
|
||||
|
@ -212,6 +226,7 @@ public class ClientResource {
|
|||
logger.debug("getClientSecret");
|
||||
UserCredentialModel model = UserCredentialModel.secret(client.getSecret());
|
||||
if (model == null) throw new NotFoundException("Client does not have a secret");
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return ModelToRepresentation.toRepresentation(model);
|
||||
}
|
||||
|
||||
|
@ -222,12 +237,12 @@ public class ClientResource {
|
|||
*/
|
||||
@Path("scope-mappings")
|
||||
public ScopeMappedResource getScopeMappedResource() {
|
||||
return new ScopeMappedResource(realm, auth, client, session);
|
||||
return new ScopeMappedResource(realm, auth, client, session, adminEvent);
|
||||
}
|
||||
|
||||
@Path("roles")
|
||||
public RoleContainerResource getRoleContainerResource() {
|
||||
return new RoleContainerResource(realm, auth, client);
|
||||
return new RoleContainerResource(realm, auth, client, adminEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -243,7 +258,7 @@ public class ClientResource {
|
|||
public Set<String> getAllowedOrigins()
|
||||
{
|
||||
auth.requireView();
|
||||
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return client.getWebOrigins();
|
||||
}
|
||||
|
||||
|
@ -261,6 +276,7 @@ public class ClientResource {
|
|||
auth.requireManage();
|
||||
|
||||
client.setWebOrigins(allowedOrigins);
|
||||
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(client).success();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -279,6 +295,7 @@ public class ClientResource {
|
|||
for (String origin : allowedOrigins) {
|
||||
client.removeWebOrigin(origin);
|
||||
}
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -289,9 +306,11 @@ public class ClientResource {
|
|||
@POST
|
||||
public GlobalRequestResult pushRevocation() {
|
||||
auth.requireManage();
|
||||
return new ResourceAdminManager(session).pushClientRevocationPolicy(uriInfo.getRequestUri(), realm, client);
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
|
||||
return new ResourceAdminManager(session).pushClientRevocationPolicy(uriInfo.getRequestUri(), realm, client);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Number of user sessions associated with this client
|
||||
*
|
||||
|
@ -309,6 +328,7 @@ public class ClientResource {
|
|||
auth.requireView();
|
||||
Map<String, Integer> map = new HashMap<String, Integer>();
|
||||
map.put("count", session.sessions().getActiveUserSessions(client.getRealm(), client));
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return map;
|
||||
}
|
||||
|
||||
|
@ -330,6 +350,7 @@ public class ClientResource {
|
|||
UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(userSession);
|
||||
sessions.add(rep);
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return sessions;
|
||||
}
|
||||
|
||||
|
@ -341,7 +362,9 @@ public class ClientResource {
|
|||
@POST
|
||||
public GlobalRequestResult logoutAll() {
|
||||
auth.requireManage();
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
|
||||
return new ResourceAdminManager(session).logoutClient(uriInfo.getRequestUri(), realm, client);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -356,7 +379,9 @@ public class ClientResource {
|
|||
if (user == null) {
|
||||
throw new NotFoundException("User not found");
|
||||
}
|
||||
new ResourceAdminManager(session).logoutUserFromClient(uriInfo.getRequestUri(), realm, client, user);
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
|
||||
new ResourceAdminManager(session).logoutUserFromClient(uriInfo.getRequestUri(), realm, client, user);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -376,6 +401,7 @@ public class ClientResource {
|
|||
}
|
||||
if (logger.isDebugEnabled()) logger.debug("Register node: " + node);
|
||||
client.registerNode(node, Time.currentTime());
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -394,8 +420,8 @@ public class ClientResource {
|
|||
if (time == null) {
|
||||
throw new NotFoundException("Client does not have a node " + node);
|
||||
}
|
||||
|
||||
client.unregisterNode(node);
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -408,9 +434,10 @@ public class ClientResource {
|
|||
@NoCache
|
||||
public GlobalRequestResult testNodesAvailable() {
|
||||
auth.requireManage();
|
||||
logger.debug("Test availability of cluster nodes");
|
||||
|
||||
logger.debug("Test availability of cluster nodes");
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
|
||||
return new ResourceAdminManager(session).testNodesAvailability(uriInfo.getRequestUri(), realm, client);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.services.resources.admin;
|
||||
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
|
@ -8,8 +9,8 @@ import org.keycloak.models.RealmModel;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ClientsByIdResource extends ClientsResource {
|
||||
public ClientsByIdResource(RealmModel realm, RealmAuth auth) {
|
||||
super(realm, auth);
|
||||
public ClientsByIdResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
|
||||
super(realm, auth, adminEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -4,6 +4,8 @@ import org.jboss.logging.Logger;
|
|||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
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.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -36,13 +39,15 @@ public class ClientsResource {
|
|||
protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
|
||||
protected RealmModel realm;
|
||||
private RealmAuth auth;
|
||||
|
||||
private AdminEventBuilder adminEvent;
|
||||
|
||||
@Context
|
||||
protected KeycloakSession session;
|
||||
|
||||
public ClientsResource(RealmModel realm, RealmAuth auth) {
|
||||
public ClientsResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
|
||||
this.realm = realm;
|
||||
this.auth = auth;
|
||||
this.adminEvent = adminEvent;
|
||||
|
||||
auth.init(RealmAuth.Resource.CLIENT);
|
||||
}
|
||||
|
@ -71,7 +76,7 @@ public class ClientsResource {
|
|||
rep.add(client);
|
||||
}
|
||||
}
|
||||
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return rep;
|
||||
}
|
||||
|
||||
|
@ -89,6 +94,9 @@ public class ClientsResource {
|
|||
|
||||
try {
|
||||
ClientModel clientModel = RepresentationToModel.createClient(session, realm, rep, true);
|
||||
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getAbsolutePathBuilder()
|
||||
.path(getClientPath(clientModel)).build().toString().substring(uriInfo.getBaseUri().toString().length()))
|
||||
.representation(rep).success();
|
||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(getClientPath(clientModel)).build()).build();
|
||||
} catch (ModelDuplicateException e) {
|
||||
return ErrorResponse.exists("Client " + rep.getClientId() + " already exists");
|
||||
|
@ -111,8 +119,9 @@ public class ClientsResource {
|
|||
if (clientModel == null) {
|
||||
throw new NotFoundException("Could not find client: " + name);
|
||||
}
|
||||
ClientResource clientResource = new ClientResource(realm, auth, clientModel, session);
|
||||
ClientResource clientResource = new ClientResource(realm, auth, clientModel, session, adminEvent);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(clientResource);
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return clientResource;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ import org.jboss.resteasy.spi.NotFoundException;
|
|||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.IdentityProviderFactory;
|
||||
import org.keycloak.broker.provider.IdentityProviderMapper;
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
|
@ -13,7 +15,6 @@ import org.keycloak.models.IdentityProviderModel;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
|
@ -25,7 +26,6 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
|||
import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.services.ErrorResponse;
|
||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||
import org.keycloak.social.SocialIdentityProvider;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
|
@ -41,6 +41,7 @@ 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.HashMap;
|
||||
import java.util.LinkedList;
|
||||
|
@ -58,14 +59,16 @@ public class IdentityProviderResource {
|
|||
private final RealmModel realm;
|
||||
private final KeycloakSession session;
|
||||
private final IdentityProviderModel identityProviderModel;
|
||||
|
||||
private final AdminEventBuilder adminEvent;
|
||||
|
||||
@Context private UriInfo uriInfo;
|
||||
|
||||
public IdentityProviderResource(RealmAuth auth, RealmModel realm, KeycloakSession session, IdentityProviderModel identityProviderModel) {
|
||||
public IdentityProviderResource(RealmAuth auth, RealmModel realm, KeycloakSession session, IdentityProviderModel identityProviderModel, AdminEventBuilder adminEvent) {
|
||||
this.realm = realm;
|
||||
this.session = session;
|
||||
this.identityProviderModel = identityProviderModel;
|
||||
this.auth = auth;
|
||||
this.adminEvent = adminEvent;
|
||||
}
|
||||
|
||||
@GET
|
||||
|
@ -74,7 +77,9 @@ public class IdentityProviderResource {
|
|||
public IdentityProviderRepresentation getIdentityProvider() {
|
||||
this.auth.requireView();
|
||||
IdentityProviderRepresentation rep = ModelToRepresentation.toRepresentation(this.identityProviderModel);
|
||||
|
||||
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
|
@ -84,7 +89,9 @@ public class IdentityProviderResource {
|
|||
this.auth.requireManage();
|
||||
|
||||
this.realm.removeIdentityProviderByAlias(this.identityProviderModel.getAlias());
|
||||
|
||||
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
|
||||
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
|
@ -108,7 +115,9 @@ public class IdentityProviderResource {
|
|||
|
||||
updateUsersAfterProviderAliasChange(this.session.users().getUsers(this.realm), oldProviderId, newProviderId);
|
||||
}
|
||||
|
||||
|
||||
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(providerRep).success();
|
||||
|
||||
return Response.noContent().build();
|
||||
} catch (ModelDuplicateException e) {
|
||||
return ErrorResponse.exists("Identity Provider " + providerRep.getAlias() + " already exists");
|
||||
|
@ -164,6 +173,7 @@ public class IdentityProviderResource {
|
|||
try {
|
||||
this.auth.requireView();
|
||||
IdentityProviderFactory factory = getIdentityProviderFactory();
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
|
||||
return factory.create(identityProviderModel).export(uriInfo, realm, format);
|
||||
} catch (Exception e) {
|
||||
return ErrorResponse.error("Could not export public broker configuration for identity provider [" + identityProviderModel.getProviderId() + "].", Response.Status.NOT_FOUND);
|
||||
|
@ -202,6 +212,7 @@ public class IdentityProviderResource {
|
|||
}
|
||||
}
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return types;
|
||||
}
|
||||
|
||||
|
@ -215,6 +226,7 @@ public class IdentityProviderResource {
|
|||
for (IdentityProviderMapperModel model : realm.getIdentityProviderMappersByAlias(identityProviderModel.getAlias())) {
|
||||
mappers.add(ModelToRepresentation.toRepresentation(model));
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return mappers;
|
||||
}
|
||||
|
||||
|
@ -225,6 +237,9 @@ public class IdentityProviderResource {
|
|||
auth.requireManage();
|
||||
IdentityProviderMapperModel model = RepresentationToModel.toModel(mapper);
|
||||
model = realm.addIdentityProviderMapper(model);
|
||||
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getAbsolutePathBuilder()
|
||||
.path(model.getId()).build().toString().substring(uriInfo.getBaseUri().toString().length()))
|
||||
.representation(mapper).success();
|
||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
|
||||
|
||||
}
|
||||
|
@ -237,6 +252,7 @@ public class IdentityProviderResource {
|
|||
auth.requireView();
|
||||
IdentityProviderMapperModel model = realm.getIdentityProviderMapperById(id);
|
||||
if (model == null) throw new NotFoundException("Model not found");
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return ModelToRepresentation.toRepresentation(model);
|
||||
}
|
||||
|
||||
|
@ -250,6 +266,8 @@ public class IdentityProviderResource {
|
|||
if (model == null) throw new NotFoundException("Model not found");
|
||||
model = RepresentationToModel.toModel(rep);
|
||||
realm.updateIdentityProviderMapper(model);
|
||||
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(rep).success();
|
||||
|
||||
}
|
||||
|
||||
@DELETE
|
||||
|
@ -260,6 +278,8 @@ public class IdentityProviderResource {
|
|||
IdentityProviderMapperModel model = realm.getIdentityProviderMapperById(id);
|
||||
if (model == null) throw new NotFoundException("Model not found");
|
||||
realm.removeIdentityProviderMapper(model);
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -6,8 +6,10 @@ import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
|
|||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.IdentityProviderFactory;
|
||||
import org.keycloak.connections.httpclient.HttpClientProvider;
|
||||
import org.keycloak.broker.provider.IdentityProviderFactory;
|
||||
import org.keycloak.connections.httpclient.HttpClientProvider;
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
|
@ -29,6 +31,7 @@ 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.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
|
@ -45,12 +48,14 @@ public class IdentityProvidersResource {
|
|||
private final RealmModel realm;
|
||||
private final KeycloakSession session;
|
||||
private RealmAuth auth;
|
||||
private AdminEventBuilder adminEvent;
|
||||
|
||||
public IdentityProvidersResource(RealmModel realm, KeycloakSession session, RealmAuth auth) {
|
||||
public IdentityProvidersResource(RealmModel realm, KeycloakSession session, RealmAuth auth, AdminEventBuilder adminEvent) {
|
||||
this.realm = realm;
|
||||
this.session = session;
|
||||
this.auth = auth;
|
||||
this.auth.init(RealmAuth.Resource.IDENTITY_PROVIDER);
|
||||
this.adminEvent = adminEvent;
|
||||
}
|
||||
|
||||
@Path("/providers/{provider_id}")
|
||||
|
@ -62,9 +67,9 @@ public class IdentityProvidersResource {
|
|||
IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
|
||||
|
||||
if (providerFactory != null) {
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return Response.ok(providerFactory).build();
|
||||
}
|
||||
|
||||
return Response.status(BAD_REQUEST).build();
|
||||
}
|
||||
|
||||
|
@ -80,6 +85,9 @@ public class IdentityProvidersResource {
|
|||
InputStream inputStream = file.getBody(InputStream.class, null);
|
||||
IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
|
||||
Map<String, String> config = providerFactory.parseConfig(inputStream);
|
||||
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).representation(config).success();
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
|
@ -97,13 +105,14 @@ public class IdentityProvidersResource {
|
|||
IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
|
||||
Map<String, String> config;
|
||||
config = providerFactory.parseConfig(inputStream);
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).representation(config).success();
|
||||
return config;
|
||||
} finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
|
@ -118,7 +127,7 @@ public class IdentityProvidersResource {
|
|||
for (IdentityProviderModel identityProviderModel : realm.getIdentityProviders()) {
|
||||
representations.add(ModelToRepresentation.toRepresentation(identityProviderModel));
|
||||
}
|
||||
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return representations;
|
||||
}
|
||||
|
||||
|
@ -130,7 +139,11 @@ public class IdentityProvidersResource {
|
|||
|
||||
try {
|
||||
this.realm.addIdentityProvider(RepresentationToModel.toModel(representation));
|
||||
|
||||
|
||||
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getAbsolutePathBuilder()
|
||||
.path(representation.getProviderId()).build().toString().substring(uriInfo.getBaseUri().toString().length()))
|
||||
.representation(representation).success();
|
||||
|
||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(representation.getProviderId()).build()).build();
|
||||
} catch (ModelDuplicateException e) {
|
||||
return ErrorResponse.exists("Identity Provider " + representation.getAlias() + " already exists");
|
||||
|
@ -153,9 +166,10 @@ public class IdentityProvidersResource {
|
|||
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, adminEvent);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(identityProviderResource);
|
||||
|
||||
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return identityProviderResource;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ package org.keycloak.services.resources.admin;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
|
@ -22,6 +24,7 @@ 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.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -37,6 +40,8 @@ public class ProtocolMappersResource {
|
|||
protected ClientModel client;
|
||||
|
||||
protected RealmAuth auth;
|
||||
|
||||
protected AdminEventBuilder adminEvent;
|
||||
|
||||
@Context
|
||||
protected UriInfo uriInfo;
|
||||
|
@ -44,9 +49,10 @@ public class ProtocolMappersResource {
|
|||
@Context
|
||||
protected KeycloakSession session;
|
||||
|
||||
public ProtocolMappersResource(ClientModel client, RealmAuth auth) {
|
||||
public ProtocolMappersResource(ClientModel client, RealmAuth auth, AdminEventBuilder adminEvent) {
|
||||
this.auth = auth;
|
||||
this.client = client;
|
||||
this.adminEvent = adminEvent;
|
||||
|
||||
auth.init(RealmAuth.Resource.USER);
|
||||
}
|
||||
|
@ -67,6 +73,7 @@ public class ProtocolMappersResource {
|
|||
for (ProtocolMapperModel mapper : client.getProtocolMappers()) {
|
||||
if (mapper.getProtocol().equals(protocol)) mappers.add(ModelToRepresentation.toRepresentation(mapper));
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return mappers;
|
||||
}
|
||||
|
||||
|
@ -83,6 +90,9 @@ public class ProtocolMappersResource {
|
|||
auth.requireManage();
|
||||
ProtocolMapperModel model = RepresentationToModel.toModel(rep);
|
||||
model = client.addProtocolMapper(model);
|
||||
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getAbsolutePathBuilder()
|
||||
.path(model.getId()).build().toString().substring(uriInfo.getBaseUri().toString().length()))
|
||||
.representation(rep).success();
|
||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
|
||||
}
|
||||
/**
|
||||
|
@ -95,10 +105,12 @@ public class ProtocolMappersResource {
|
|||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public void createMapper(List<ProtocolMapperRepresentation> reps) {
|
||||
auth.requireManage();
|
||||
ProtocolMapperModel model = null;
|
||||
for (ProtocolMapperRepresentation rep : reps) {
|
||||
ProtocolMapperModel model = RepresentationToModel.toModel(rep);
|
||||
model = RepresentationToModel.toModel(rep);
|
||||
model = client.addProtocolMapper(model);
|
||||
}
|
||||
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getPath()).representation(reps).success();
|
||||
}
|
||||
|
||||
@GET
|
||||
|
@ -111,6 +123,7 @@ public class ProtocolMappersResource {
|
|||
for (ProtocolMapperModel mapper : client.getProtocolMappers()) {
|
||||
mappers.add(ModelToRepresentation.toRepresentation(mapper));
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return mappers;
|
||||
}
|
||||
|
||||
|
@ -122,6 +135,7 @@ public class ProtocolMappersResource {
|
|||
auth.requireView();
|
||||
ProtocolMapperModel model = client.getProtocolMapperById(id);
|
||||
if (model == null) throw new NotFoundException("Model not found");
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return ModelToRepresentation.toRepresentation(model);
|
||||
}
|
||||
|
||||
|
@ -135,6 +149,7 @@ public class ProtocolMappersResource {
|
|||
if (model == null) throw new NotFoundException("Model not found");
|
||||
model = RepresentationToModel.toModel(rep);
|
||||
client.updateProtocolMapper(model);
|
||||
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(rep).success();
|
||||
}
|
||||
|
||||
@DELETE
|
||||
|
@ -145,6 +160,8 @@ public class ProtocolMappersResource {
|
|||
ProtocolMapperModel model = client.getProtocolMapperById(id);
|
||||
if (model == null) throw new NotFoundException("Model not found");
|
||||
client.removeProtocolMapper(model);
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,10 +5,15 @@ import org.jboss.resteasy.annotations.cache.NoCache;
|
|||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.ClientConnection;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventQuery;
|
||||
import org.keycloak.events.EventStoreProvider;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.admin.AdminEventQuery;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.exportimport.ClientImporter;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -46,6 +51,7 @@ import javax.ws.rs.core.HttpHeaders;
|
|||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
@ -63,6 +69,7 @@ public class RealmAdminResource {
|
|||
protected RealmAuth auth;
|
||||
protected RealmModel realm;
|
||||
private TokenManager tokenManager;
|
||||
private AdminEventBuilder adminEvent;
|
||||
|
||||
@Context
|
||||
protected KeycloakSession session;
|
||||
|
@ -76,10 +83,11 @@ public class RealmAdminResource {
|
|||
@Context
|
||||
protected HttpHeaders headers;
|
||||
|
||||
public RealmAdminResource(RealmAuth auth, RealmModel realm, TokenManager tokenManager) {
|
||||
public RealmAdminResource(RealmAuth auth, RealmModel realm, TokenManager tokenManager, AdminEventBuilder adminEvent) {
|
||||
this.auth = auth;
|
||||
this.realm = realm;
|
||||
this.tokenManager = tokenManager;
|
||||
this.adminEvent = adminEvent;
|
||||
|
||||
auth.init(RealmAuth.Resource.REALM);
|
||||
}
|
||||
|
@ -102,7 +110,7 @@ public class RealmAdminResource {
|
|||
*/
|
||||
@Path("clients")
|
||||
public ClientsResource getClients() {
|
||||
ClientsResource clientsResource = new ClientsResource(realm, auth);
|
||||
ClientsResource clientsResource = new ClientsResource(realm, auth, adminEvent);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(clientsResource);
|
||||
return clientsResource;
|
||||
}
|
||||
|
@ -114,7 +122,7 @@ public class RealmAdminResource {
|
|||
*/
|
||||
@Path("clients-by-id")
|
||||
public ClientsByIdResource getClientsById() {
|
||||
ClientsByIdResource clientsResource = new ClientsByIdResource(realm, auth);
|
||||
ClientsByIdResource clientsResource = new ClientsByIdResource(realm, auth, adminEvent);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(clientsResource);
|
||||
return clientsResource;
|
||||
}
|
||||
|
@ -126,7 +134,7 @@ public class RealmAdminResource {
|
|||
*/
|
||||
@Path("roles")
|
||||
public RoleContainerResource getRoleContainerResource() {
|
||||
return new RoleContainerResource(realm, auth, realm);
|
||||
return new RoleContainerResource(realm, auth, realm, adminEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -148,12 +156,14 @@ public class RealmAdminResource {
|
|||
CacheUserProvider cache = (CacheUserProvider)session.userStorage();
|
||||
rep.setUserCacheEnabled(cache.isEnabled());
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return rep;
|
||||
} else {
|
||||
auth.requireAny();
|
||||
|
||||
RealmRepresentation rep = new RealmRepresentation();
|
||||
rep.setRealm(realm.getName());
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return rep;
|
||||
}
|
||||
}
|
||||
|
@ -188,7 +198,8 @@ public class RealmAdminResource {
|
|||
for (final UserFederationProviderModel fedProvider : federationProviders) {
|
||||
usersSyncManager.refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), fedProvider, realm.getId());
|
||||
}
|
||||
|
||||
|
||||
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(rep).success();
|
||||
return Response.noContent().build();
|
||||
} catch (PatternSyntaxException e) {
|
||||
return ErrorResponse.exists("Specified regex pattern(s) is invalid.");
|
||||
|
@ -209,6 +220,8 @@ public class RealmAdminResource {
|
|||
|
||||
if (!new RealmManager(session).removeRealm(realm)) {
|
||||
throw new NotFoundException("Realm doesn't exist");
|
||||
} else {
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +232,7 @@ public class RealmAdminResource {
|
|||
*/
|
||||
@Path("users")
|
||||
public UsersResource users() {
|
||||
UsersResource users = new UsersResource(realm, auth, tokenManager);
|
||||
UsersResource users = new UsersResource(realm, auth, tokenManager, adminEvent);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(users);
|
||||
//resourceContext.initResource(users);
|
||||
return users;
|
||||
|
@ -227,7 +240,7 @@ public class RealmAdminResource {
|
|||
|
||||
@Path("user-federation")
|
||||
public UserFederationResource userFederation() {
|
||||
UserFederationResource fed = new UserFederationResource(realm, auth);
|
||||
UserFederationResource fed = new UserFederationResource(realm, auth, adminEvent);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(fed);
|
||||
//resourceContext.initResource(fed);
|
||||
return fed;
|
||||
|
@ -240,7 +253,7 @@ public class RealmAdminResource {
|
|||
*/
|
||||
@Path("roles-by-id")
|
||||
public RoleByIdResource rolesById() {
|
||||
RoleByIdResource resource = new RoleByIdResource(realm, auth);
|
||||
RoleByIdResource resource = new RoleByIdResource(realm, auth, adminEvent);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||
//resourceContext.initResource(resource);
|
||||
return resource;
|
||||
|
@ -254,7 +267,8 @@ public class RealmAdminResource {
|
|||
@POST
|
||||
public GlobalRequestResult pushRevocation() {
|
||||
auth.requireManage();
|
||||
return new ResourceAdminManager(session).pushRealmRevocationPolicy(uriInfo.getRequestUri(), realm);
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
|
||||
return new ResourceAdminManager(session).pushRealmRevocationPolicy(uriInfo.getRequestUri(), realm);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -266,7 +280,8 @@ public class RealmAdminResource {
|
|||
@POST
|
||||
public GlobalRequestResult logoutAll() {
|
||||
session.sessions().removeUserSessions(realm);
|
||||
return new ResourceAdminManager(session).logoutAll(uriInfo.getRequestUri(), realm);
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
|
||||
return new ResourceAdminManager(session).logoutAll(uriInfo.getRequestUri(), realm);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -279,8 +294,10 @@ public class RealmAdminResource {
|
|||
@DELETE
|
||||
public void deleteSession(@PathParam("session") String sessionId) {
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
|
||||
if (userSession == null) throw new NotFoundException("Sesssion not found");
|
||||
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, connection, headers, true);
|
||||
if (userSession == null) throw new NotFoundException("Sesssion not found");
|
||||
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, connection, headers, true);
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -302,6 +319,7 @@ public class RealmAdminResource {
|
|||
if (size == 0) continue;
|
||||
stats.put(client.getClientId(), size);
|
||||
}
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).representation(stats).success();
|
||||
return stats;
|
||||
}
|
||||
|
||||
|
@ -327,6 +345,7 @@ public class RealmAdminResource {
|
|||
map.put("active", size + "");
|
||||
data.add(map);
|
||||
}
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).representation(data).success();
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -366,6 +385,8 @@ public class RealmAdminResource {
|
|||
* @param client app or oauth client name
|
||||
* @param user user id
|
||||
* @param ipAddress
|
||||
* @param dateTo
|
||||
* @param dateFrom
|
||||
* @param firstResult
|
||||
* @param maxResults
|
||||
* @return
|
||||
|
@ -419,6 +440,86 @@ public class RealmAdminResource {
|
|||
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query admin events. Returns all admin events, or will query based on URL query parameters listed here
|
||||
*
|
||||
* @param client app or oauth client name
|
||||
* @param operationTypes operation type
|
||||
* @param authUser user id
|
||||
* @param authIpAddress
|
||||
* @param resourcePath
|
||||
* @param dateTo
|
||||
* @param dateFrom
|
||||
* @param resourcePath
|
||||
* @param firstResult
|
||||
* @param maxResults
|
||||
* @return
|
||||
*/
|
||||
@Path("admin-events")
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public List<AdminEvent> getEvents(@QueryParam("authRealm") String authRealm, @QueryParam("authClient") String authClient,
|
||||
@QueryParam("authUser") String authUser, @QueryParam("authIpAddress") String authIpAddress,
|
||||
@QueryParam("resourcePath") String resourcePath, @QueryParam("dateFrom") String dateFrom,
|
||||
@QueryParam("dateTo") String dateTo, @QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResults) {
|
||||
auth.init(RealmAuth.Resource.EVENTS).requireView();
|
||||
|
||||
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
|
||||
AdminEventQuery query = null;
|
||||
|
||||
if(realm.getName().equals(Config.getAdminRealm())) {
|
||||
query = eventStore.createAdminQuery();
|
||||
if(authRealm != null) {
|
||||
query.authRealm(authRealm);
|
||||
}
|
||||
} else {
|
||||
query = eventStore.createAdminQuery().authRealm(realm.getId());
|
||||
}
|
||||
|
||||
if (authClient != null) {
|
||||
query.authClient(authClient);
|
||||
}
|
||||
|
||||
if (authUser != null) {
|
||||
query.authUser(authUser);
|
||||
}
|
||||
|
||||
if (authIpAddress != null) {
|
||||
query.authIpAddress(authIpAddress);
|
||||
}
|
||||
|
||||
if (resourcePath != null) {
|
||||
query.resourcePath(resourcePath);
|
||||
}
|
||||
|
||||
List<String> operationTypes = uriInfo.getQueryParameters().get("operationTypes");
|
||||
if (operationTypes != null) {
|
||||
OperationType[] t = new OperationType[operationTypes.size()];
|
||||
for (int i = 0; i < t.length; i++) {
|
||||
t[i] = OperationType.valueOf(operationTypes.get(i));
|
||||
}
|
||||
query.operation(t);
|
||||
}
|
||||
|
||||
if(dateFrom != null) {
|
||||
query.fromTime(dateFrom);
|
||||
}
|
||||
if(dateTo != null) {
|
||||
query.toTime(dateTo);
|
||||
}
|
||||
|
||||
if (firstResult != null) {
|
||||
query.firstResult(firstResult);
|
||||
}
|
||||
if (maxResults != null) {
|
||||
query.maxResults(maxResults);
|
||||
}
|
||||
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all events.
|
||||
|
@ -432,6 +533,19 @@ public class RealmAdminResource {
|
|||
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
|
||||
eventStore.clear(realm.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all admin events.
|
||||
*
|
||||
*/
|
||||
@Path("admin-events")
|
||||
@DELETE
|
||||
public void clearAdminEvents() {
|
||||
auth.init(RealmAuth.Resource.EVENTS).requireManage();
|
||||
|
||||
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
|
||||
eventStore.clearAdmin(realm.getId());
|
||||
}
|
||||
|
||||
@Path("testLDAPConnection")
|
||||
@GET
|
||||
|
@ -446,6 +560,6 @@ public class RealmAdminResource {
|
|||
|
||||
@Path("identity-provider")
|
||||
public IdentityProvidersResource getIdentityProviderResource() {
|
||||
return new IdentityProvidersResource(realm, session, this.auth);
|
||||
return new IdentityProvidersResource(realm, session, this.auth, adminEvent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import org.jboss.resteasy.plugins.providers.multipart.InputPart;
|
|||
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.AdminRoles;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -33,6 +35,7 @@ import javax.ws.rs.core.HttpHeaders;
|
|||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
|
@ -49,16 +52,18 @@ public class RealmsAdminResource {
|
|||
protected static final Logger logger = Logger.getLogger(RealmsAdminResource.class);
|
||||
protected AdminAuth auth;
|
||||
protected TokenManager tokenManager;
|
||||
protected AdminEventBuilder adminEvent;
|
||||
|
||||
@Context
|
||||
protected KeycloakSession session;
|
||||
|
||||
|
||||
@Context
|
||||
protected KeycloakApplication keycloak;
|
||||
|
||||
public RealmsAdminResource(AdminAuth auth, TokenManager tokenManager) {
|
||||
public RealmsAdminResource(AdminAuth auth, TokenManager tokenManager, AdminEventBuilder adminEvent) {
|
||||
this.auth = auth;
|
||||
this.tokenManager = tokenManager;
|
||||
this.adminEvent = adminEvent;
|
||||
}
|
||||
|
||||
public static final CacheControl noCache = new CacheControl();
|
||||
|
@ -87,6 +92,7 @@ public class RealmsAdminResource {
|
|||
ClientModel adminApp = auth.getRealm().getClientByClientId(realmManager.getRealmAdminClientId(auth.getRealm()));
|
||||
addRealmRep(reps, auth.getRealm(), adminApp);
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
logger.debug(("getRealms()"));
|
||||
return reps;
|
||||
}
|
||||
|
@ -128,6 +134,8 @@ public class RealmsAdminResource {
|
|||
|
||||
URI location = AdminRoot.realmsUrl(uriInfo).path(realm.getName()).build();
|
||||
logger.debugv("imported realm success, sending back: {0}", location.toString());
|
||||
|
||||
adminEvent.operation(OperationType.CREATE).resourcePath(location.toString()).representation(rep).success();
|
||||
|
||||
return Response.created(location).build();
|
||||
} catch (ModelDuplicateException e) {
|
||||
|
@ -158,10 +166,11 @@ public class RealmsAdminResource {
|
|||
|
||||
Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
|
||||
List<InputPart> inputParts = uploadForm.get("file");
|
||||
|
||||
RealmRepresentation rep = null;
|
||||
|
||||
for (InputPart inputPart : inputParts) {
|
||||
// inputPart.getBody doesn't work as content-type is wrong, and inputPart.setMediaType is not supported on AS7 (RestEasy 2.3.2.Final)
|
||||
RealmRepresentation rep = JsonSerialization.readValue(inputPart.getBodyAsString(), RealmRepresentation.class);
|
||||
rep = JsonSerialization.readValue(inputPart.getBodyAsString(), RealmRepresentation.class);
|
||||
RealmModel realm;
|
||||
try {
|
||||
realm = realmManager.importRealm(rep);
|
||||
|
@ -170,13 +179,15 @@ public class RealmsAdminResource {
|
|||
}
|
||||
|
||||
grantPermissionsToRealmCreator(realm);
|
||||
|
||||
|
||||
URI location = null;
|
||||
if (inputParts.size() == 1) {
|
||||
URI location = AdminRoot.realmsUrl(uriInfo).path(realm.getName()).build();
|
||||
location = AdminRoot.realmsUrl(uriInfo).path(realm.getName()).build();
|
||||
adminEvent.operation(OperationType.CREATE).resourcePath(location.toString()).representation(rep).success();
|
||||
return Response.created(location).build();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
|
@ -219,7 +230,7 @@ public class RealmsAdminResource {
|
|||
realmAuth = new RealmAuth(auth, realm.getClientByClientId(realmManager.getRealmAdminClientId(auth.getRealm())));
|
||||
}
|
||||
|
||||
RealmAdminResource adminResource = new RealmAdminResource(realmAuth, realm, tokenManager);
|
||||
RealmAdminResource adminResource = new RealmAdminResource(realmAuth, realm, tokenManager, adminEvent);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(adminResource);
|
||||
//resourceContext.initResource(adminResource);
|
||||
return adminResource;
|
||||
|
|
|
@ -3,6 +3,8 @@ package org.keycloak.services.resources.admin;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -20,6 +22,8 @@ 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.UriInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -33,15 +37,17 @@ public class RoleByIdResource extends RoleResource {
|
|||
protected static final Logger logger = Logger.getLogger(RoleByIdResource.class);
|
||||
private final RealmModel realm;
|
||||
private final RealmAuth auth;
|
||||
private AdminEventBuilder adminEvent;
|
||||
|
||||
@Context
|
||||
protected KeycloakSession session;
|
||||
|
||||
public RoleByIdResource(RealmModel realm, RealmAuth auth) {
|
||||
public RoleByIdResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
|
||||
super(realm);
|
||||
|
||||
this.realm = realm;
|
||||
this.auth = auth;
|
||||
this.adminEvent = adminEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,6 +63,8 @@ public class RoleByIdResource extends RoleResource {
|
|||
public RoleRepresentation getRole(final @PathParam("role-id") String id) {
|
||||
RoleModel roleModel = getRoleModel(id);
|
||||
auth.requireView();
|
||||
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
|
||||
return getRole(roleModel);
|
||||
}
|
||||
|
@ -76,6 +84,8 @@ public class RoleByIdResource extends RoleResource {
|
|||
r = RealmAuth.Resource.USER;
|
||||
}
|
||||
auth.init(r);
|
||||
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
|
||||
return roleModel;
|
||||
}
|
||||
|
@ -92,6 +102,7 @@ public class RoleByIdResource extends RoleResource {
|
|||
RoleModel role = getRoleModel(id);
|
||||
auth.requireManage();
|
||||
deleteRole(role);
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,6 +118,7 @@ public class RoleByIdResource extends RoleResource {
|
|||
RoleModel role = getRoleModel(id);
|
||||
auth.requireManage();
|
||||
updateRole(rep, role);
|
||||
adminEvent.operation(OperationType.UPDATE).resourcePath(session.getContext().getUri().getPath()).representation(rep).success();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -122,6 +134,8 @@ public class RoleByIdResource extends RoleResource {
|
|||
RoleModel role = getRoleModel(id);
|
||||
auth.requireManage();
|
||||
addComposites(roles, role);
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri().getPath()).representation(roles).success();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -139,6 +153,7 @@ public class RoleByIdResource extends RoleResource {
|
|||
if (logger.isDebugEnabled()) logger.debug("*** getRoleComposites: '" + id + "'");
|
||||
RoleModel role = getRoleModel(id);
|
||||
auth.requireView();
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return getRoleComposites(role);
|
||||
}
|
||||
|
||||
|
@ -155,6 +170,7 @@ public class RoleByIdResource extends RoleResource {
|
|||
public Set<RoleRepresentation> getRealmRoleComposites(final @PathParam("role-id") String id) {
|
||||
RoleModel role = getRoleModel(id);
|
||||
auth.requireView();
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return getRealmRoleComposites(role);
|
||||
}
|
||||
|
||||
|
@ -178,6 +194,7 @@ public class RoleByIdResource extends RoleResource {
|
|||
throw new NotFoundException("Could not find client: " + appName);
|
||||
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return getClientRoleComposites(app, role);
|
||||
}
|
||||
|
||||
|
@ -201,6 +218,7 @@ public class RoleByIdResource extends RoleResource {
|
|||
throw new NotFoundException("Could not find client: " + appId);
|
||||
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return getClientRoleComposites(app, role);
|
||||
}
|
||||
|
||||
|
@ -217,6 +235,7 @@ public class RoleByIdResource extends RoleResource {
|
|||
RoleModel role = getRoleModel(id);
|
||||
auth.requireManage();
|
||||
deleteComposites(roles, role);
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,10 @@ package org.keycloak.services.resources.admin;
|
|||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
|
@ -23,6 +26,7 @@ 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;
|
||||
import java.util.Set;
|
||||
|
@ -35,12 +39,14 @@ public class RoleContainerResource extends RoleResource {
|
|||
private final RealmModel realm;
|
||||
private final RealmAuth auth;
|
||||
protected RoleContainerModel roleContainer;
|
||||
private AdminEventBuilder adminEvent;
|
||||
|
||||
public RoleContainerResource(RealmModel realm, RealmAuth auth, RoleContainerModel roleContainer) {
|
||||
public RoleContainerResource(RealmModel realm, RealmAuth auth, RoleContainerModel roleContainer, AdminEventBuilder adminEvent) {
|
||||
super(realm);
|
||||
this.realm = realm;
|
||||
this.auth = auth;
|
||||
this.roleContainer = roleContainer;
|
||||
this.adminEvent = adminEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,7 +57,7 @@ public class RoleContainerResource extends RoleResource {
|
|||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public List<RoleRepresentation> getRoles() {
|
||||
public List<RoleRepresentation> getRoles(@Context final UriInfo uriInfo) {
|
||||
auth.requireAny();
|
||||
|
||||
Set<RoleModel> roleModels = roleContainer.getRoles();
|
||||
|
@ -59,6 +65,7 @@ public class RoleContainerResource extends RoleResource {
|
|||
for (RoleModel roleModel : roleModels) {
|
||||
roles.add(ModelToRepresentation.toRepresentation(roleModel));
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return roles;
|
||||
}
|
||||
|
||||
|
@ -77,6 +84,11 @@ public class RoleContainerResource extends RoleResource {
|
|||
try {
|
||||
RoleModel role = roleContainer.addRole(rep.getName());
|
||||
role.setDescription(rep.getDescription());
|
||||
|
||||
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getAbsolutePathBuilder()
|
||||
.path(role.getName()).build().toString().substring(uriInfo.getBaseUri().toString().length()))
|
||||
.representation(rep).success();
|
||||
|
||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(role.getName()).build()).build();
|
||||
} catch (ModelDuplicateException e) {
|
||||
return ErrorResponse.exists("Role with name " + rep.getName() + " already exists");
|
||||
|
@ -93,7 +105,7 @@ public class RoleContainerResource extends RoleResource {
|
|||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public RoleRepresentation getRole(final @PathParam("role-name") String roleName) {
|
||||
public RoleRepresentation getRole(@Context final UriInfo uriInfo, final @PathParam("role-name") String roleName) {
|
||||
auth.requireView();
|
||||
|
||||
RoleModel roleModel = roleContainer.getRole(roleName);
|
||||
|
@ -101,6 +113,8 @@ public class RoleContainerResource extends RoleResource {
|
|||
throw new NotFoundException("Could not find role: " + roleName);
|
||||
}
|
||||
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
|
||||
return getRole(roleModel);
|
||||
}
|
||||
|
||||
|
@ -112,15 +126,18 @@ public class RoleContainerResource extends RoleResource {
|
|||
@Path("{role-name}")
|
||||
@DELETE
|
||||
@NoCache
|
||||
public void deleteRole(final @PathParam("role-name") String roleName) {
|
||||
public void deleteRole(@Context final UriInfo uriInfo, final @PathParam("role-name") String roleName) {
|
||||
auth.requireManage();
|
||||
|
||||
RoleRepresentation rep = getRole(roleName);
|
||||
RoleRepresentation rep = getRole(uriInfo, roleName);
|
||||
RoleModel role = roleContainer.getRole(roleName);
|
||||
if (role == null) {
|
||||
throw new NotFoundException("Could not find role: " + roleName);
|
||||
}
|
||||
deleteRole(role);
|
||||
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,7 +150,7 @@ public class RoleContainerResource extends RoleResource {
|
|||
@Path("{role-name}")
|
||||
@PUT
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Response updateRole(final @PathParam("role-name") String roleName, final RoleRepresentation rep) {
|
||||
public Response updateRole(@Context final UriInfo uriInfo, final @PathParam("role-name") String roleName, final RoleRepresentation rep) {
|
||||
auth.requireManage();
|
||||
|
||||
RoleModel role = roleContainer.getRole(roleName);
|
||||
|
@ -142,6 +159,9 @@ public class RoleContainerResource extends RoleResource {
|
|||
}
|
||||
try {
|
||||
updateRole(rep, role);
|
||||
|
||||
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(rep).success();
|
||||
|
||||
return Response.noContent().build();
|
||||
} catch (ModelDuplicateException e) {
|
||||
return ErrorResponse.exists("Role with name " + rep.getName() + " already exists");
|
||||
|
@ -157,7 +177,7 @@ public class RoleContainerResource extends RoleResource {
|
|||
@Path("{role-name}/composites")
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public void addComposites(final @PathParam("role-name") String roleName, List<RoleRepresentation> roles) {
|
||||
public void addComposites(@Context final UriInfo uriInfo, final @PathParam("role-name") String roleName, List<RoleRepresentation> roles) {
|
||||
auth.requireManage();
|
||||
|
||||
RoleModel role = roleContainer.getRole(roleName);
|
||||
|
@ -165,6 +185,8 @@ public class RoleContainerResource extends RoleResource {
|
|||
throw new NotFoundException("Could not find role: " + roleName);
|
||||
}
|
||||
addComposites(roles, role);
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).representation(roles).success();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -177,13 +199,14 @@ public class RoleContainerResource extends RoleResource {
|
|||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Set<RoleRepresentation> getRoleComposites(final @PathParam("role-name") String roleName) {
|
||||
public Set<RoleRepresentation> getRoleComposites(@Context final UriInfo uriInfo, final @PathParam("role-name") String roleName) {
|
||||
auth.requireManage();
|
||||
|
||||
RoleModel role = roleContainer.getRole(roleName);
|
||||
if (role == null) {
|
||||
throw new NotFoundException("Could not find role: " + roleName);
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return getRoleComposites(role);
|
||||
}
|
||||
|
||||
|
@ -197,13 +220,14 @@ public class RoleContainerResource extends RoleResource {
|
|||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Set<RoleRepresentation> getRealmRoleComposites(final @PathParam("role-name") String roleName) {
|
||||
public Set<RoleRepresentation> getRealmRoleComposites(@Context final UriInfo uriInfo, final @PathParam("role-name") String roleName) {
|
||||
auth.requireManage();
|
||||
|
||||
RoleModel role = roleContainer.getRole(roleName);
|
||||
if (role == null) {
|
||||
throw new NotFoundException("Could not find role: " + roleName);
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return getRealmRoleComposites(role);
|
||||
}
|
||||
|
||||
|
@ -218,7 +242,8 @@ public class RoleContainerResource extends RoleResource {
|
|||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Set<RoleRepresentation> getClientRoleComposites(final @PathParam("role-name") String roleName,
|
||||
public Set<RoleRepresentation> getClientRoleComposites(@Context final UriInfo uriInfo,
|
||||
final @PathParam("role-name") String roleName,
|
||||
final @PathParam("clientId") String clientId) {
|
||||
auth.requireManage();
|
||||
|
||||
|
@ -231,6 +256,7 @@ public class RoleContainerResource extends RoleResource {
|
|||
throw new NotFoundException("Could not find client: " + clientId);
|
||||
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return getClientRoleComposites(app, role);
|
||||
}
|
||||
|
||||
|
@ -246,7 +272,8 @@ public class RoleContainerResource extends RoleResource {
|
|||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Set<RoleRepresentation> getClientByIdRoleComposites(final @PathParam("role-name") String roleName,
|
||||
public Set<RoleRepresentation> getClientByIdRoleComposites(@Context final UriInfo uriInfo,
|
||||
final @PathParam("role-name") String roleName,
|
||||
final @PathParam("id") String id) {
|
||||
auth.requireManage();
|
||||
|
||||
|
@ -259,6 +286,7 @@ public class RoleContainerResource extends RoleResource {
|
|||
throw new NotFoundException("Could not find client: " + id);
|
||||
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return getClientRoleComposites(client, role);
|
||||
}
|
||||
|
||||
|
@ -272,7 +300,9 @@ public class RoleContainerResource extends RoleResource {
|
|||
@Path("{role-name}/composites")
|
||||
@DELETE
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public void deleteComposites(final @PathParam("role-name") String roleName, List<RoleRepresentation> roles) {
|
||||
public void deleteComposites(@Context final UriInfo uriInfo,
|
||||
final @PathParam("role-name") String roleName,
|
||||
List<RoleRepresentation> roles) {
|
||||
auth.requireManage();
|
||||
|
||||
RoleModel role = roleContainer.getRole(roleName);
|
||||
|
@ -280,6 +310,7 @@ public class RoleContainerResource extends RoleResource {
|
|||
throw new NotFoundException("Could not find role: " + roleName);
|
||||
}
|
||||
deleteComposites(roles, role);
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@ package org.keycloak.services.resources.admin;
|
|||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -15,7 +17,10 @@ import javax.ws.rs.GET;
|
|||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -30,13 +35,15 @@ public class ScopeMappedClientResource {
|
|||
protected ClientModel client;
|
||||
protected KeycloakSession session;
|
||||
protected ClientModel scopedClient;
|
||||
|
||||
public ScopeMappedClientResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session, ClientModel scopedClient) {
|
||||
protected AdminEventBuilder adminEvent;
|
||||
|
||||
public ScopeMappedClientResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session, ClientModel scopedClient, AdminEventBuilder adminEvent) {
|
||||
this.realm = realm;
|
||||
this.auth = auth;
|
||||
this.client = client;
|
||||
this.session = session;
|
||||
this.scopedClient = scopedClient;
|
||||
this.adminEvent = adminEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,6 +62,7 @@ public class ScopeMappedClientResource {
|
|||
for (RoleModel roleModel : mappings) {
|
||||
mapRep.add(ModelToRepresentation.toRepresentation(roleModel));
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return mapRep;
|
||||
}
|
||||
|
||||
|
@ -71,6 +79,7 @@ public class ScopeMappedClientResource {
|
|||
auth.requireView();
|
||||
|
||||
Set<RoleModel> roles = scopedClient.getRoles();
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return ScopeMappedResource.getAvailable(client, roles);
|
||||
}
|
||||
|
||||
|
@ -87,6 +96,7 @@ public class ScopeMappedClientResource {
|
|||
auth.requireView();
|
||||
|
||||
Set<RoleModel> roles = scopedClient.getRoles();
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return ScopeMappedResource.getComposite(client, roles);
|
||||
}
|
||||
|
||||
|
@ -107,6 +117,7 @@ public class ScopeMappedClientResource {
|
|||
}
|
||||
client.addScopeMapping(roleModel);
|
||||
}
|
||||
adminEvent.operation(OperationType.CREATE).resourcePath(session.getContext().getUri().getPath()).representation(roles).success();
|
||||
|
||||
}
|
||||
|
||||
|
@ -135,5 +146,6 @@ public class ScopeMappedClientResource {
|
|||
client.deleteScopeMapping(roleModel);
|
||||
}
|
||||
}
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package org.keycloak.services.resources.admin;
|
|||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -18,7 +20,10 @@ 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.UriInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -36,12 +41,14 @@ public class ScopeMappedResource {
|
|||
private RealmAuth auth;
|
||||
protected ClientModel client;
|
||||
protected KeycloakSession session;
|
||||
protected AdminEventBuilder adminEvent;
|
||||
|
||||
public ScopeMappedResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session) {
|
||||
public ScopeMappedResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session, AdminEventBuilder adminEvent) {
|
||||
this.realm = realm;
|
||||
this.auth = auth;
|
||||
this.client = client;
|
||||
this.session = session;
|
||||
this.adminEvent = adminEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,6 +91,7 @@ public class ScopeMappedResource {
|
|||
}
|
||||
}
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return all;
|
||||
}
|
||||
|
||||
|
@ -104,6 +112,7 @@ public class ScopeMappedResource {
|
|||
for (RoleModel roleModel : realmMappings) {
|
||||
realmMappingsRep.add(ModelToRepresentation.toRepresentation(roleModel));
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return realmMappingsRep;
|
||||
}
|
||||
|
||||
|
@ -120,6 +129,7 @@ public class ScopeMappedResource {
|
|||
auth.requireView();
|
||||
|
||||
Set<RoleModel> roles = realm.getRoles();
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return getAvailable(client, roles);
|
||||
}
|
||||
|
||||
|
@ -147,6 +157,7 @@ public class ScopeMappedResource {
|
|||
auth.requireView();
|
||||
|
||||
Set<RoleModel> roles = realm.getRoles();
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return getComposite(client, roles);
|
||||
}
|
||||
|
||||
|
@ -176,7 +187,7 @@ public class ScopeMappedResource {
|
|||
}
|
||||
client.addScopeMapping(roleModel);
|
||||
}
|
||||
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri().getPath()).representation(roles).success();
|
||||
|
||||
}
|
||||
|
||||
|
@ -206,6 +217,8 @@ public class ScopeMappedResource {
|
|||
client.deleteScopeMapping(roleModel);
|
||||
}
|
||||
}
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
|
||||
}
|
||||
|
||||
@Path("clients/{clientId}")
|
||||
|
@ -215,8 +228,8 @@ public class ScopeMappedResource {
|
|||
if (app == null) {
|
||||
throw new NotFoundException("Role not found");
|
||||
}
|
||||
|
||||
return new ScopeMappedClientResource(realm, auth, client, session, app);
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return new ScopeMappedClientResource(realm, auth, client, session, app, adminEvent);
|
||||
}
|
||||
|
||||
@Path("clients-by-id/{id}")
|
||||
|
@ -226,7 +239,7 @@ public class ScopeMappedResource {
|
|||
if (app == null) {
|
||||
throw new NotFoundException("Client not found");
|
||||
}
|
||||
|
||||
return new ScopeMappedClientResource(realm, auth, client, session, app);
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return new ScopeMappedClientResource(realm, auth, client, session, app, adminEvent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import org.keycloak.broker.provider.IdentityProvider;
|
|||
import org.keycloak.broker.provider.IdentityProviderFactory;
|
||||
import org.keycloak.events.EventListenerProvider;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.exportimport.ClientImporter;
|
||||
import org.keycloak.exportimport.ClientImporterFactory;
|
||||
import org.keycloak.freemarker.Theme;
|
||||
|
@ -39,6 +40,8 @@ import java.util.Set;
|
|||
*/
|
||||
public class ServerInfoAdminResource {
|
||||
|
||||
private static final Map<String, List<String>> ENUMS = createEnumsMap(EventType.class, OperationType.class);
|
||||
|
||||
@Context
|
||||
private KeycloakSession session;
|
||||
|
||||
|
@ -61,7 +64,7 @@ public class ServerInfoAdminResource {
|
|||
setProviders(info);
|
||||
setProtocolMapperTypes(info);
|
||||
setBuiltinProtocolMappers(info);
|
||||
setEventTypes(info);
|
||||
info.setEnums(ENUMS);
|
||||
return info;
|
||||
}
|
||||
|
||||
|
@ -181,15 +184,6 @@ 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 {
|
||||
|
||||
private String version;
|
||||
|
@ -209,7 +203,7 @@ public class ServerInfoAdminResource {
|
|||
private Map<String, List<ProtocolMapperTypeRepresentation>> protocolMapperTypes;
|
||||
private Map<String, List<ProtocolMapperRepresentation>> builtinProtocolMappers;
|
||||
|
||||
private List<String> eventTypes;
|
||||
private Map<String, List<String>> enums;
|
||||
|
||||
public ServerInfoRepresentation() {
|
||||
}
|
||||
|
@ -262,13 +256,30 @@ public class ServerInfoAdminResource {
|
|||
this.builtinProtocolMappers = builtinProtocolMappers;
|
||||
}
|
||||
|
||||
public List<String> getEventTypes() {
|
||||
return eventTypes;
|
||||
public Map<String, List<String>> getEnums() {
|
||||
return enums;
|
||||
}
|
||||
|
||||
public void setEventTypes(List<String> eventTypes) {
|
||||
this.eventTypes = eventTypes;
|
||||
public void setEnums(Map<String, List<String>> enums) {
|
||||
this.enums = enums;
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, List<String>> createEnumsMap(Class... enums) {
|
||||
Map<String, List<String>> m = new HashMap<>();
|
||||
for (Class e : enums) {
|
||||
String n = e.getSimpleName();
|
||||
n = Character.toLowerCase(n.charAt(0)) + n.substring(1);
|
||||
|
||||
List<String> l = new LinkedList<>();
|
||||
for (Object c : e.getEnumConstants()) {
|
||||
l.add(c.toString());
|
||||
}
|
||||
Collections.sort(l);
|
||||
|
||||
m.put(n, l);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,7 +3,10 @@ package org.keycloak.services.resources.admin;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -16,7 +19,10 @@ import javax.ws.rs.GET;
|
|||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -33,12 +39,18 @@ public class UserClientRoleMappingsResource {
|
|||
protected RealmAuth auth;
|
||||
protected UserModel user;
|
||||
protected ClientModel client;
|
||||
protected AdminEventBuilder adminEvent;
|
||||
|
||||
@Context
|
||||
protected KeycloakSession session;
|
||||
|
||||
|
||||
public UserClientRoleMappingsResource(RealmModel realm, RealmAuth auth, UserModel user, ClientModel client) {
|
||||
public UserClientRoleMappingsResource(RealmModel realm, RealmAuth auth, UserModel user, ClientModel client, AdminEventBuilder adminEvent) {
|
||||
this.realm = realm;
|
||||
this.auth = auth;
|
||||
this.user = user;
|
||||
this.client = client;
|
||||
this.adminEvent = adminEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,6 +69,7 @@ public class UserClientRoleMappingsResource {
|
|||
for (RoleModel roleModel : mappings) {
|
||||
mapRep.add(ModelToRepresentation.toRepresentation(roleModel));
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return mapRep;
|
||||
}
|
||||
|
||||
|
@ -77,6 +90,7 @@ public class UserClientRoleMappingsResource {
|
|||
for (RoleModel roleModel : roles) {
|
||||
if (user.hasRole(roleModel)) mapRep.add(ModelToRepresentation.toRepresentation(roleModel));
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return mapRep;
|
||||
}
|
||||
|
||||
|
@ -93,6 +107,7 @@ public class UserClientRoleMappingsResource {
|
|||
auth.requireView();
|
||||
|
||||
Set<RoleModel> available = client.getRoles();
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
return getAvailableRoles(user, available);
|
||||
}
|
||||
|
||||
|
@ -127,6 +142,7 @@ public class UserClientRoleMappingsResource {
|
|||
}
|
||||
user.grantRole(roleModel);
|
||||
}
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri().getPath()).representation(roles).success();
|
||||
|
||||
}
|
||||
|
||||
|
@ -159,5 +175,6 @@ public class UserClientRoleMappingsResource {
|
|||
user.deleteRoleMapping(roleModel);
|
||||
}
|
||||
}
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri().getPath()).success();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import org.jboss.logging.Logger;
|
|||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.NotFoundException;
|
||||
import org.keycloak.constants.KerberosConstants;
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
|
@ -31,6 +33,7 @@ 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.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -46,6 +49,8 @@ public class UserFederationResource {
|
|||
protected RealmModel realm;
|
||||
|
||||
protected RealmAuth auth;
|
||||
|
||||
protected AdminEventBuilder adminEvent;
|
||||
|
||||
@Context
|
||||
protected UriInfo uriInfo;
|
||||
|
@ -53,10 +58,11 @@ public class UserFederationResource {
|
|||
@Context
|
||||
protected KeycloakSession session;
|
||||
|
||||
public UserFederationResource(RealmModel realm, RealmAuth auth) {
|
||||
public UserFederationResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
|
||||
this.auth = auth;
|
||||
this.realm = realm;
|
||||
|
||||
this.adminEvent = adminEvent;
|
||||
|
||||
auth.init(RealmAuth.Resource.USER);
|
||||
}
|
||||
|
||||
|
@ -78,6 +84,7 @@ public class UserFederationResource {
|
|||
rep.setOptions(((UserFederationProviderFactory)factory).getConfigurationOptions());
|
||||
providers.add(rep);
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return providers;
|
||||
}
|
||||
|
||||
|
@ -99,6 +106,9 @@ public class UserFederationResource {
|
|||
UserFederationProviderFactoryRepresentation rep = new UserFederationProviderFactoryRepresentation();
|
||||
rep.setId(factory.getId());
|
||||
rep.setOptions(((UserFederationProviderFactory)factory).getConfigurationOptions());
|
||||
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
|
||||
return rep;
|
||||
}
|
||||
throw new NotFoundException("Could not find provider");
|
||||
|
@ -123,6 +133,11 @@ public class UserFederationResource {
|
|||
rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
|
||||
new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
|
||||
checkKerberosCredential(model);
|
||||
|
||||
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getAbsolutePathBuilder()
|
||||
.path(model.getId()).build().toString().substring(uriInfo.getBaseUri().toString().length()))
|
||||
.representation(rep).success();
|
||||
|
||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
|
||||
}
|
||||
|
||||
|
@ -146,6 +161,9 @@ public class UserFederationResource {
|
|||
realm.updateUserFederationProvider(model);
|
||||
new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
|
||||
checkKerberosCredential(model);
|
||||
|
||||
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(rep).success();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,10 +179,10 @@ public class UserFederationResource {
|
|||
auth.requireView();
|
||||
for (UserFederationProviderModel model : realm.getUserFederationProviders()) {
|
||||
if (model.getId().equals(id)) {
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return ModelToRepresentation.toRepresentation(model);
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundException("could not find provider");
|
||||
}
|
||||
|
||||
|
@ -182,6 +200,9 @@ public class UserFederationResource {
|
|||
UserFederationProviderModel model = new UserFederationProviderModel(id, null, null, -1, null, -1, -1, 0);
|
||||
realm.removeUserFederationProvider(model);
|
||||
new UsersSyncManager().removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), model);
|
||||
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -201,6 +222,7 @@ public class UserFederationResource {
|
|||
UserFederationProviderRepresentation rep = ModelToRepresentation.toRepresentation(model);
|
||||
reps.add(rep);
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return reps;
|
||||
}
|
||||
|
||||
|
@ -224,6 +246,7 @@ public class UserFederationResource {
|
|||
} else if ("triggerChangedUsersSync".equals(action)) {
|
||||
syncManager.syncChangedUsers(session.getKeycloakSessionFactory(), realm.getId(), model);
|
||||
}
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
|
||||
return Response.noContent().build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import org.jboss.resteasy.spi.NotFoundException;
|
|||
import org.keycloak.ClientConnection;
|
||||
import org.keycloak.email.EmailException;
|
||||
import org.keycloak.email.EmailProvider;
|
||||
import org.keycloak.events.AdminEventBuilder;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.Constants;
|
||||
|
@ -56,6 +58,7 @@ import javax.ws.rs.core.MediaType;
|
|||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -78,6 +81,8 @@ public class UsersResource {
|
|||
|
||||
private RealmAuth auth;
|
||||
|
||||
private AdminEventBuilder adminEvent;
|
||||
|
||||
@Context
|
||||
protected ClientConnection clientConnection;
|
||||
|
||||
|
@ -90,9 +95,10 @@ public class UsersResource {
|
|||
@Context
|
||||
protected HttpHeaders headers;
|
||||
|
||||
public UsersResource(RealmModel realm, RealmAuth auth, TokenManager tokenManager) {
|
||||
public UsersResource(RealmModel realm, RealmAuth auth, TokenManager tokenManager, AdminEventBuilder adminEvent) {
|
||||
this.auth = auth;
|
||||
this.realm = realm;
|
||||
this.adminEvent = adminEvent;
|
||||
|
||||
auth.init(RealmAuth.Resource.USER);
|
||||
}
|
||||
|
@ -116,11 +122,11 @@ public class UsersResource {
|
|||
throw new NotFoundException("User not found");
|
||||
}
|
||||
updateUserFromRep(user, rep);
|
||||
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(rep).success();
|
||||
|
||||
if (session.getTransaction().isActive()) {
|
||||
session.getTransaction().commit();
|
||||
}
|
||||
|
||||
return Response.noContent().build();
|
||||
} catch (ModelDuplicateException e) {
|
||||
return ErrorResponse.exists("User exists with same username or email");
|
||||
|
@ -152,11 +158,15 @@ public class UsersResource {
|
|||
try {
|
||||
UserModel user = session.users().addUser(realm, rep.getUsername());
|
||||
updateUserFromRep(user, rep);
|
||||
|
||||
|
||||
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getAbsolutePathBuilder()
|
||||
.path(user.getUsername()).build().toString().substring(uriInfo.getBaseUri().toString().length()))
|
||||
.representation(rep).success();
|
||||
|
||||
if (session.getTransaction().isActive()) {
|
||||
session.getTransaction().commit();
|
||||
}
|
||||
|
||||
|
||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(user.getUsername()).build()).build();
|
||||
} catch (ModelDuplicateException e) {
|
||||
if (session.getTransaction().isActive()) {
|
||||
|
@ -217,6 +227,8 @@ public class UsersResource {
|
|||
if (user == null) {
|
||||
throw new NotFoundException("User not found");
|
||||
}
|
||||
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
|
||||
UserRepresentation rep = ModelToRepresentation.toRepresentation(user);
|
||||
|
||||
|
@ -256,6 +268,7 @@ public class UsersResource {
|
|||
UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(session);
|
||||
reps.add(rep);
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return reps;
|
||||
}
|
||||
|
||||
|
@ -287,6 +300,7 @@ public class UsersResource {
|
|||
}
|
||||
}
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -305,7 +319,7 @@ public class UsersResource {
|
|||
|
||||
FederatedIdentityModel socialLink = new FederatedIdentityModel(provider, rep.getUserId(), rep.getUserName());
|
||||
session.users().addFederatedIdentity(realm, user, socialLink);
|
||||
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).representation(rep).success();
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
|
@ -321,6 +335,7 @@ public class UsersResource {
|
|||
if (!session.users().removeFederatedIdentity(realm, user, provider)) {
|
||||
throw new NotFoundException("Link not found");
|
||||
}
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -347,6 +362,7 @@ public class UsersResource {
|
|||
UserConsentRepresentation rep = ModelToRepresentation.toRepresentation(consent);
|
||||
result.add(rep);
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -374,6 +390,7 @@ public class UsersResource {
|
|||
} else {
|
||||
throw new NotFoundException("Consent not found for user " + username + " and client " + clientId);
|
||||
}
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -395,6 +412,7 @@ public class UsersResource {
|
|||
for (UserSessionModel userSession : userSessions) {
|
||||
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, true);
|
||||
}
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -416,6 +434,7 @@ public class UsersResource {
|
|||
|
||||
boolean removed = new UserManager(session).removeUser(realm, user);
|
||||
if (removed) {
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
|
||||
return Response.noContent().build();
|
||||
} else {
|
||||
return ErrorResponse.error("User couldn't be deleted", Response.Status.BAD_REQUEST);
|
||||
|
@ -524,6 +543,7 @@ public class UsersResource {
|
|||
}
|
||||
}
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return all;
|
||||
}
|
||||
|
||||
|
@ -550,6 +570,7 @@ public class UsersResource {
|
|||
for (RoleModel roleModel : realmMappings) {
|
||||
realmMappingsRep.add(ModelToRepresentation.toRepresentation(roleModel));
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return realmMappingsRep;
|
||||
}
|
||||
|
||||
|
@ -578,6 +599,7 @@ public class UsersResource {
|
|||
realmMappingsRep.add(ModelToRepresentation.toRepresentation(roleModel));
|
||||
}
|
||||
}
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return realmMappingsRep;
|
||||
}
|
||||
|
||||
|
@ -600,6 +622,7 @@ public class UsersResource {
|
|||
}
|
||||
|
||||
Set<RoleModel> available = realm.getRoles();
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return UserClientRoleMappingsResource.getAvailableRoles(user, available);
|
||||
}
|
||||
|
||||
|
@ -628,7 +651,8 @@ public class UsersResource {
|
|||
}
|
||||
user.grantRole(roleModel);
|
||||
}
|
||||
|
||||
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).representation(roles).success();
|
||||
|
||||
}
|
||||
|
||||
|
@ -665,6 +689,8 @@ public class UsersResource {
|
|||
user.deleteRoleMapping(roleModel);
|
||||
}
|
||||
}
|
||||
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
|
||||
}
|
||||
|
||||
@Path("{username}/role-mappings/clients/{clientId}")
|
||||
|
@ -679,8 +705,8 @@ public class UsersResource {
|
|||
if (client == null) {
|
||||
throw new NotFoundException("Client not found");
|
||||
}
|
||||
|
||||
return new UserClientRoleMappingsResource(realm, auth, user, client);
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return new UserClientRoleMappingsResource(realm, auth, user, client, adminEvent);
|
||||
|
||||
}
|
||||
@Path("{username}/role-mappings/clients-by-id/{id}")
|
||||
|
@ -695,8 +721,9 @@ public class UsersResource {
|
|||
if (client == null) {
|
||||
throw new NotFoundException("Client not found");
|
||||
}
|
||||
|
||||
return new UserClientRoleMappingsResource(realm, auth, user, client);
|
||||
|
||||
adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
|
||||
return new UserClientRoleMappingsResource(realm, auth, user, client, adminEvent);
|
||||
|
||||
}
|
||||
/**
|
||||
|
@ -729,6 +756,8 @@ public class UsersResource {
|
|||
throw new BadRequestException("Can't reset password as account is read only");
|
||||
}
|
||||
if (pass.isTemporary()) user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
|
||||
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -748,6 +777,7 @@ public class UsersResource {
|
|||
}
|
||||
|
||||
user.setTotp(false);
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -823,6 +853,9 @@ public class UsersResource {
|
|||
this.session.getProvider(EmailProvider.class).setRealm(realm).setUser(user).sendPasswordReset(link, expiration);
|
||||
|
||||
//audit.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, accessCode.getCodeId()).success();
|
||||
|
||||
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
|
||||
|
||||
return Response.ok().build();
|
||||
} catch (EmailException e) {
|
||||
logger.error("Failed to send password reset email", e);
|
||||
|
|
|
@ -8,6 +8,7 @@ import org.junit.Assert;
|
|||
import org.junit.rules.TestRule;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventListenerProvider;
|
||||
|
@ -195,6 +196,12 @@ public class AssertEvents implements TestRule, EventListenerProviderFactory {
|
|||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(AdminEvent event, boolean includeRepresentation) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
package org.keycloak.testsuite.events;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.events.EventStoreProvider;
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.admin.AuthDetails;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:giriraj.sharma27@gmail.com">Giriraj Sharma</a>
|
||||
*/
|
||||
public class AdminEventStoreProviderTest {
|
||||
|
||||
@ClassRule
|
||||
public static KeycloakRule kc = new KeycloakRule();
|
||||
|
||||
private KeycloakSession session;
|
||||
|
||||
private EventStoreProvider eventStore;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
session = kc.startSession();
|
||||
eventStore = session.getProvider(EventStoreProvider.class);
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
eventStore.clearAdmin();
|
||||
kc.stopSession(session, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void save() {
|
||||
eventStore.onEvent(create(OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void query() {
|
||||
long oldest = System.currentTimeMillis() - 30000;
|
||||
long newest = System.currentTimeMillis() + 30000;
|
||||
|
||||
eventStore.onEvent(create(OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(newest, OperationType.ACTION, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(newest, OperationType.ACTION, "realmId", "clientId", "userId2", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(OperationType.VIEW, "realmId2", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(oldest, OperationType.VIEW, "realmId", "clientId2", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(OperationType.VIEW, "realmId", "clientId", "userId2", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
|
||||
resetSession();
|
||||
|
||||
Assert.assertEquals(5, eventStore.createAdminQuery().authClient("clientId").getResultList().size());
|
||||
Assert.assertEquals(5, eventStore.createAdminQuery().authRealm("realmId").getResultList().size());
|
||||
Assert.assertEquals(4, eventStore.createAdminQuery().operation(OperationType.VIEW).getResultList().size());
|
||||
Assert.assertEquals(6, eventStore.createAdminQuery().operation(OperationType.VIEW, OperationType.ACTION).getResultList().size());
|
||||
Assert.assertEquals(4, eventStore.createAdminQuery().authUser("userId").getResultList().size());
|
||||
|
||||
Assert.assertEquals(1, eventStore.createAdminQuery().authUser("userId").operation(OperationType.ACTION).getResultList().size());
|
||||
|
||||
Assert.assertEquals(2, eventStore.createAdminQuery().maxResults(2).getResultList().size());
|
||||
Assert.assertEquals(1, eventStore.createAdminQuery().firstResult(5).getResultList().size());
|
||||
|
||||
Assert.assertEquals(newest, eventStore.createAdminQuery().maxResults(1).getResultList().get(0).getTime());
|
||||
Assert.assertEquals(oldest, eventStore.createAdminQuery().firstResult(5).maxResults(1).getResultList().get(0).getTime());
|
||||
|
||||
eventStore.clearAdmin("realmId");
|
||||
eventStore.clearAdmin("realmId2");
|
||||
|
||||
Assert.assertEquals(0, eventStore.createAdminQuery().getResultList().size());
|
||||
|
||||
String d1 = new String("2015-03-04");
|
||||
String d2 = new String("2015-03-05");
|
||||
String d3 = new String("2015-03-06");
|
||||
String d4 = new String("2015-03-07");
|
||||
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Date date1 = null, date2 = null, date3 = null, date4 = null;
|
||||
|
||||
try {
|
||||
date1 = formatter.parse(d1);
|
||||
date2 = formatter.parse(d2);
|
||||
date3 = formatter.parse(d3);
|
||||
date4 = formatter.parse(d4);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
eventStore.onEvent(create(date1, OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(date1, OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(date2, OperationType.ACTION, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(date2, OperationType.ACTION, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(date3, OperationType.UPDATE, "realmId", "clientId", "userId2", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(date3, OperationType.DELETE, "realmId", "clientId", "userId2", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(date4, OperationType.CREATE, "realmId2", "clientId2", "userId2", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(date4, OperationType.CREATE, "realmId2", "clientId2", "userId2", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
|
||||
resetSession();
|
||||
|
||||
Assert.assertEquals(6, eventStore.createAdminQuery().authClient("clientId").getResultList().size());
|
||||
Assert.assertEquals(2, eventStore.createAdminQuery().authClient("clientId2").getResultList().size());
|
||||
|
||||
Assert.assertEquals(6, eventStore.createAdminQuery().authRealm("realmId").getResultList().size());
|
||||
Assert.assertEquals(2, eventStore.createAdminQuery().authRealm("realmId2").getResultList().size());
|
||||
|
||||
Assert.assertEquals(4, eventStore.createAdminQuery().authUser("userId").getResultList().size());
|
||||
Assert.assertEquals(4, eventStore.createAdminQuery().authUser("userId2").getResultList().size());
|
||||
|
||||
Assert.assertEquals(2, eventStore.createAdminQuery().operation(OperationType.VIEW).getResultList().size());
|
||||
Assert.assertEquals(2, eventStore.createAdminQuery().operation(OperationType.ACTION).getResultList().size());
|
||||
Assert.assertEquals(4, eventStore.createAdminQuery().operation(OperationType.VIEW, OperationType.ACTION).getResultList().size());
|
||||
Assert.assertEquals(1, eventStore.createAdminQuery().operation(OperationType.UPDATE).getResultList().size());
|
||||
Assert.assertEquals(1, eventStore.createAdminQuery().operation(OperationType.DELETE).getResultList().size());
|
||||
Assert.assertEquals(2, eventStore.createAdminQuery().operation(OperationType.CREATE).getResultList().size());
|
||||
|
||||
Assert.assertEquals(8, eventStore.createAdminQuery().fromTime("2015-03-04").getResultList().size());
|
||||
Assert.assertEquals(8, eventStore.createAdminQuery().toTime("2015-03-07").getResultList().size());
|
||||
|
||||
Assert.assertEquals(4, eventStore.createAdminQuery().fromTime("2015-03-06").getResultList().size());
|
||||
Assert.assertEquals(4, eventStore.createAdminQuery().toTime("2015-03-05").getResultList().size());
|
||||
|
||||
Assert.assertEquals(0, eventStore.createAdminQuery().fromTime("2015-03-08").getResultList().size());
|
||||
Assert.assertEquals(0, eventStore.createAdminQuery().toTime("2015-03-03").getResultList().size());
|
||||
|
||||
Assert.assertEquals(8, eventStore.createAdminQuery().fromTime("2015-03-04").toTime("2015-03-07").getResultList().size());
|
||||
Assert.assertEquals(6, eventStore.createAdminQuery().fromTime("2015-03-05").toTime("2015-03-07").getResultList().size());
|
||||
Assert.assertEquals(4, eventStore.createAdminQuery().fromTime("2015-03-04").toTime("2015-03-05").getResultList().size());
|
||||
Assert.assertEquals(4, eventStore.createAdminQuery().fromTime("2015-03-06").toTime("2015-03-07").getResultList().size());
|
||||
|
||||
Assert.assertEquals(0, eventStore.createAdminQuery().fromTime("2015-03-01").toTime("2015-03-03").getResultList().size());
|
||||
Assert.assertEquals(0, eventStore.createAdminQuery().fromTime("2015-03-08").toTime("2015-03-10").getResultList().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryResourcePath() {
|
||||
long oldest = System.currentTimeMillis() - 30000;
|
||||
long newest = System.currentTimeMillis() + 30000;
|
||||
|
||||
eventStore.onEvent(create(OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(newest, OperationType.ACTION, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(newest, OperationType.ACTION, "realmId", "clientId", "userId2", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(OperationType.VIEW, "realmId2", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(oldest, OperationType.VIEW, "realmId", "clientId2", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(OperationType.VIEW, "realmId", "clientId", "userId2", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
|
||||
resetSession();
|
||||
|
||||
Assert.assertEquals(6, eventStore.createAdminQuery().resourcePath("/admin").getResultList().size());
|
||||
Assert.assertEquals(6, eventStore.createAdminQuery().resourcePath("/realms").getResultList().size());
|
||||
Assert.assertEquals(6, eventStore.createAdminQuery().resourcePath("/master").getResultList().size());
|
||||
Assert.assertEquals(6, eventStore.createAdminQuery().resourcePath("/admin/realms").getResultList().size());
|
||||
Assert.assertEquals(6, eventStore.createAdminQuery().resourcePath("/realms/master").getResultList().size());
|
||||
Assert.assertEquals(6, eventStore.createAdminQuery().resourcePath("/admin/realms/master").getResultList().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clear() {
|
||||
eventStore.onEvent(create(System.currentTimeMillis() - 30000, OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(System.currentTimeMillis() - 20000, OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(System.currentTimeMillis(), OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(System.currentTimeMillis(), OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(System.currentTimeMillis() - 30000, OperationType.VIEW, "realmId2", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
|
||||
resetSession();
|
||||
|
||||
eventStore.clearAdmin("realmId");
|
||||
|
||||
Assert.assertEquals(1, eventStore.createAdminQuery().getResultList().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearOld() {
|
||||
eventStore.onEvent(create(System.currentTimeMillis() - 30000, OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(System.currentTimeMillis() - 20000, OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(System.currentTimeMillis(), OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(System.currentTimeMillis(), OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
eventStore.onEvent(create(System.currentTimeMillis() - 30000, OperationType.VIEW, "realmId2", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
|
||||
|
||||
resetSession();
|
||||
|
||||
eventStore.clearAdmin("realmId", System.currentTimeMillis() - 10000);
|
||||
|
||||
Assert.assertEquals(3, eventStore.createAdminQuery().getResultList().size());
|
||||
}
|
||||
|
||||
private AdminEvent create(OperationType operation, String realmId, String clientId, String userId, String ipAddress, String resourcePath, String error) {
|
||||
return create(System.currentTimeMillis(), operation, realmId, clientId, userId, ipAddress, resourcePath, error);
|
||||
}
|
||||
|
||||
private AdminEvent create(Date date, OperationType operation, String realmId, String clientId, String userId, String ipAddress, String resourcePath, String error) {
|
||||
return create(date.getTime(), operation, realmId, clientId, userId, ipAddress, resourcePath, error);
|
||||
}
|
||||
|
||||
private AdminEvent create(long time, OperationType operation, String realmId, String clientId, String userId, String ipAddress, String resourcePath, String error) {
|
||||
AdminEvent e = new AdminEvent();
|
||||
e.setTime(time);
|
||||
e.setOperationType(operation);
|
||||
AuthDetails authDetails = new AuthDetails();
|
||||
authDetails.setRealmId(realmId);
|
||||
authDetails.setClientId(clientId);
|
||||
authDetails.setUserId(userId);
|
||||
authDetails.setIpAddress(ipAddress);
|
||||
e.setAuthDetails(authDetails);
|
||||
e.setResourcePath(resourcePath);
|
||||
e.setError(error);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
private void resetSession() {
|
||||
kc.stopSession(session, true);
|
||||
session = kc.startSession();
|
||||
eventStore = session.getProvider(EventStoreProvider.class);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue