@@ -84,6 +84,7 @@ protected interface Actions {
8484 * {@code plugin-security.policy}. So we may as well have gradle set the property.
8585 */
8686 private static final Path AUDIT_LOG_FILE = lookupAuditLog ();
87+ private static final Path ROLLED_OVER_AUDIT_LOG_FILE = lookupRolledOverAuditLog ();
8788
8889 @ SuppressForbidden (reason ="security doesn't work with mock filesystem" )
8990 private static Path lookupAuditLog () {
@@ -95,6 +96,16 @@ private static Path lookupAuditLog() {
9596 }
9697 return Paths .get (auditLogFileString );
9798 }
99+
100+ @ SuppressForbidden (reason ="security doesn't work with mock filesystem" )
101+ private static Path lookupRolledOverAuditLog () {
102+ String auditLogFileString = System .getProperty ("tests.audit.yesterday.logfile" );
103+ if (null == auditLogFileString ) {
104+ throw new IllegalStateException ("tests.audit.yesterday.logfile must be set to run this test. It should be automatically "
105+ + "set by gradle." );
106+ }
107+ return Paths .get (auditLogFileString );
108+ }
98109
99110 private static boolean oneTimeSetup = false ;
100111 private static boolean auditFailure = false ;
@@ -107,7 +118,12 @@ private static Path lookupAuditLog() {
107118 /**
108119 * How much of the audit log was written before the test started.
109120 */
110- private long auditLogWrittenBeforeTestStart ;
121+ private static long auditLogWrittenBeforeTestStart ;
122+
123+ /**
124+ * If the audit log file rolled over. This is a rare case possible only at midnight.
125+ */
126+ private static boolean auditFileRolledOver = false ;
111127
112128 public SqlSecurityTestCase (Actions actions ) {
113129 this .actions = actions ;
@@ -164,7 +180,7 @@ public void setInitialAuditLogOffset() {
164180 return null ;
165181 }
166182 if (false == Files .isRegularFile (AUDIT_LOG_FILE )) {
167- throw new IllegalStateException ("expected tests.audit.logfile [" + AUDIT_LOG_FILE + "]to be a plain file but wasn't" );
183+ throw new IllegalStateException ("expected tests.audit.logfile [" + AUDIT_LOG_FILE + "] to be a plain file but wasn't" );
168184 }
169185 try {
170186 auditLogWrittenBeforeTestStart = Files .size (AUDIT_LOG_FILE );
@@ -556,51 +572,85 @@ public void assertLogs() throws Exception {
556572 if (sm != null ) {
557573 sm .checkPermission (new SpecialPermission ());
558574 }
559- BufferedReader logReader = AccessController .doPrivileged ((PrivilegedAction <BufferedReader >) () -> {
575+
576+ BufferedReader [] logReaders = new BufferedReader [2 ];
577+ AccessController .doPrivileged ((PrivilegedAction <Void >) () -> {
560578 try {
561- return Files .newBufferedReader (AUDIT_LOG_FILE , StandardCharsets .UTF_8 );
579+ // the audit log file rolled over during the test
580+ // and we need to consume the rest of the rolled over file plus the new audit log file
581+ if (auditFileRolledOver == false && Files .exists (ROLLED_OVER_AUDIT_LOG_FILE )) {
582+ // once the audit file rolled over, it will stay like this
583+ auditFileRolledOver = true ;
584+ // the order in the array matters, as the readers will be used in that order
585+ logReaders [0 ] = Files .newBufferedReader (ROLLED_OVER_AUDIT_LOG_FILE , StandardCharsets .UTF_8 );
586+ }
587+ logReaders [1 ] = Files .newBufferedReader (AUDIT_LOG_FILE , StandardCharsets .UTF_8 );
588+ return null ;
562589 } catch (IOException e ) {
563590 throw new RuntimeException (e );
564591 }
565592 });
566- logReader .skip (auditLogWrittenBeforeTestStart );
593+
594+ // The "index" is used as a way of reading from both rolled over file and current audit file in order: rolled over file
595+ // first, then the audit log file. Very rarely we will read from the rolled over file: when the test happened to run
596+ // at midnight and the audit file rolled over during the test.
597+ int index ;
598+ if (logReaders [0 ] != null ) {
599+ logReaders [0 ].skip (auditLogWrittenBeforeTestStart );
600+ // start with the rolled over file first
601+ index = 0 ;
602+ } else {
603+ // the current audit log file reader should always be non-null
604+ logReaders [1 ].skip (auditLogWrittenBeforeTestStart );
605+ // start with the current audit logging file
606+ index = 1 ;
607+ }
567608
568609 List <Map <String , Object >> logs = new ArrayList <>();
569610 String line ;
570- while ((line = logReader .readLine ()) != null ) {
571- try {
572- final Map <String , Object > log = XContentHelper .convertToMap (JsonXContent .jsonXContent , line , false );
573- if (false == ("access_denied" .equals (log .get ("event.action" ))
574- || "access_granted" .equals (log .get ("event.action" )))) {
575- continue ;
576- }
577- assertThat (log .containsKey ("action" ), is (true ));
578- if (false == (SQL_ACTION_NAME .equals (log .get ("action" ))
579- || GetIndexAction .NAME .equals (log .get ("action" ))
580- || FieldCapabilitiesAction .NAME .equals (log .get ("action" )))) {
581- // TODO we may want to extend this and the assertions to SearchAction.NAME as well
582- continue ;
611+ while (index < 2 ) {
612+ line = logReaders [index ].readLine ();
613+ // when the end of the file is reached, either stop or move to the next reader
614+ if (line == null ) {
615+ if (++index == 2 ) {
616+ break ;
583617 }
584- assertThat (log .containsKey ("user.name" ), is (true ));
585- List <String > indices = new ArrayList <>();
586- if (log .containsKey ("indices" )) {
587- indices = (ArrayList <String >) log .get ("indices" );
588- if ("test_admin" .equals (log .get ("user.name" ))) {
589- /*
590- * Sometimes we accidentally sneak access to the security tables. This is fine,
591- * SQL drops them from the interface. So we might have access to them, but we
592- * don't show them.
593- */
594- indices .remove (".security" );
595- indices .remove (".security-6" );
618+ }
619+ else {
620+ try {
621+ final Map <String , Object > log = XContentHelper .convertToMap (JsonXContent .jsonXContent , line , false );
622+ if (false == ("access_denied" .equals (log .get ("event.action" ))
623+ || "access_granted" .equals (log .get ("event.action" )))) {
624+ continue ;
625+ }
626+ assertThat (log .containsKey ("action" ), is (true ));
627+ if (false == (SQL_ACTION_NAME .equals (log .get ("action" ))
628+ || GetIndexAction .NAME .equals (log .get ("action" ))
629+ || FieldCapabilitiesAction .NAME .equals (log .get ("action" )))) {
630+ // TODO we may want to extend this and the assertions to SearchAction.NAME as well
631+ continue ;
632+ }
633+ assertThat (log .containsKey ("user.name" ), is (true ));
634+ List <String > indices = new ArrayList <>();
635+ if (log .containsKey ("indices" )) {
636+ indices = (ArrayList <String >) log .get ("indices" );
637+ if ("test_admin" .equals (log .get ("user.name" ))) {
638+ /*
639+ * Sometimes we accidentally sneak access to the security tables. This is fine,
640+ * SQL drops them from the interface. So we might have access to them, but we
641+ * don't show them.
642+ */
643+ indices .remove (".security" );
644+ indices .remove (".security-6" );
645+ }
596646 }
647+ // Use a sorted list for indices for consistent error reporting
648+ Collections .sort (indices );
649+ log .put ("indices" , indices );
650+ logs .add (log );
651+ } catch (final ElasticsearchParseException e ) {
652+ throw new IllegalArgumentException ("Unrecognized log: " + line , e );
597653 }
598- // Use a sorted list for indices for consistent error reporting
599- Collections .sort (indices );
600- log .put ("indices" , indices );
601- logs .add (log );
602- } catch (final ElasticsearchParseException e ) {
603- throw new IllegalArgumentException ("Unrecognized log: " + line , e );
604654 }
605655 }
606656 List <Map <String , Object >> allLogs = new ArrayList <>(logs );
0 commit comments