feat(auth): Phase 2 — audited-users UserPicker + Tier-3 Keycloak Admin lookup
This commit is contained in:
+25
@@ -61,4 +61,29 @@ public interface AuditLogRepository extends JpaRepository<AuditLog, Long> {
|
||||
@Param("from") OffsetDateTime from,
|
||||
@Param("to") OffsetDateTime to,
|
||||
Pageable pageable);
|
||||
|
||||
/**
|
||||
* Distinct user_id значения из всего audit log. Используется
|
||||
* {@code UserDisplayService.listAuditedUsers} для построения
|
||||
* UserPicker'а в /audit (фильтрация по «тем, кто реально появлялся
|
||||
* в audit», а не по всем юзерам realm).
|
||||
*
|
||||
* <p>Сортировка по самому свежему {@code event_time} — недавно
|
||||
* активные юзеры сверху picker'а.
|
||||
*
|
||||
* <p>Лимит передаём через {@link Pageable} в controller'е — JPQL
|
||||
* не умеет {@code LIMIT} в обычном SELECT-выражении.
|
||||
*/
|
||||
@Query("""
|
||||
SELECT a.userId
|
||||
FROM AuditLog a
|
||||
WHERE a.userId IS NOT NULL
|
||||
GROUP BY a.userId
|
||||
ORDER BY MAX(a.eventTime) DESC
|
||||
""")
|
||||
List<String> findDistinctUserIds(Pageable pageable);
|
||||
|
||||
default List<String> findDistinctUserIds(int limit) {
|
||||
return findDistinctUserIds(org.springframework.data.domain.PageRequest.of(0, Math.max(1, limit)));
|
||||
}
|
||||
}
|
||||
|
||||
+10
-8
@@ -17,13 +17,15 @@ import java.time.OffsetDateTime;
|
||||
*
|
||||
* <p>Source semantics:
|
||||
* <ul>
|
||||
* <li>{@link Source#JWT_CAPTURE} — written by JwtUserCaptureFilter
|
||||
* on every authenticated request. Highest accuracy (claims as
|
||||
* the user actually saw them).</li>
|
||||
* <li>{@link Source#KEYCLOAK_SYNC} — written by the periodic
|
||||
* Phase 3 scheduled bulk sync from Keycloak Admin API.</li>
|
||||
* <li>{@link Source#KEYCLOAK_ON_DEMAND} — written when a cache miss
|
||||
* triggers a one-shot Keycloak Admin lookup. Phase 2.</li>
|
||||
* <li>{@link Source#KEYCLOAK_SYNC} — written by the scheduled bulk
|
||||
* refresh job from Keycloak Admin API ({@code UserDisplayCacheRefreshJob},
|
||||
* default every 6h). Primary populator after Phase 2.</li>
|
||||
* <li>{@link Source#KEYCLOAK_ON_DEMAND} — written when a cache miss on
|
||||
* {@code find(sub)} triggers a one-shot Keycloak Admin lookup.</li>
|
||||
* <li>{@link Source#JWT_CAPTURE} — legacy. Previously written by
|
||||
* {@code JwtUserCaptureFilter} on every authenticated request;
|
||||
* filter removed in Phase 2. Enum value retained for historical
|
||||
* rows in existing prod databases.</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Entity
|
||||
@@ -45,7 +47,7 @@ public class UserDisplayCacheEntry {
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "source", nullable = false, length = 32)
|
||||
private Source source = Source.JWT_CAPTURE;
|
||||
private Source source = Source.KEYCLOAK_SYNC;
|
||||
|
||||
@Column(name = "synced_at", nullable = false)
|
||||
private OffsetDateTime syncedAt;
|
||||
|
||||
Reference in New Issue
Block a user