3 namespace Tests\Actions;
5 use BookStack\Actions\Activity;
6 use BookStack\Actions\ActivityLogger;
7 use BookStack\Actions\ActivityType;
8 use BookStack\Auth\UserRepo;
9 use BookStack\Entities\Models\Chapter;
10 use BookStack\Entities\Models\Page;
11 use BookStack\Entities\Repos\PageRepo;
12 use BookStack\Entities\Tools\TrashCan;
16 class AuditLogTest extends TestCase
18 /** @var ActivityLogger */
19 protected $activityService;
21 protected function setUp(): void
24 $this->activityService = app(ActivityLogger::class);
27 public function test_only_accessible_with_right_permissions()
29 $viewer = $this->getViewer();
30 $this->actingAs($viewer);
32 $resp = $this->get('/settings/audit');
33 $this->assertPermissionError($resp);
35 $this->giveUserPermissions($viewer, ['settings-manage']);
36 $resp = $this->get('/settings/audit');
37 $this->assertPermissionError($resp);
39 $this->giveUserPermissions($viewer, ['users-manage']);
40 $resp = $this->get('/settings/audit');
41 $resp->assertStatus(200);
42 $resp->assertSeeText('Audit Log');
45 public function test_shows_activity()
47 $admin = $this->getAdmin();
48 $this->actingAs($admin);
49 $page = Page::query()->first();
50 $this->activityService->add(ActivityType::PAGE_CREATE, $page);
51 $activity = Activity::query()->orderBy('id', 'desc')->first();
53 $resp = $this->get('settings/audit');
54 $resp->assertSeeText($page->name);
55 $resp->assertSeeText('page_create');
56 $resp->assertSeeText($activity->created_at->toDateTimeString());
57 $this->withHtml($resp)->assertElementContains('.table-user-item', $admin->name);
60 public function test_shows_name_for_deleted_items()
62 $this->actingAs($this->getAdmin());
63 $page = Page::query()->first();
64 $pageName = $page->name;
65 $this->activityService->add(ActivityType::PAGE_CREATE, $page);
67 app(PageRepo::class)->destroy($page);
68 app(TrashCan::class)->empty();
70 $resp = $this->get('settings/audit');
71 $resp->assertSeeText('Deleted Item');
72 $resp->assertSeeText('Name: ' . $pageName);
75 public function test_shows_activity_for_deleted_users()
77 $viewer = $this->getViewer();
78 $this->actingAs($viewer);
79 $page = Page::query()->first();
80 $this->activityService->add(ActivityType::PAGE_CREATE, $page);
82 $this->actingAs($this->getAdmin());
83 app(UserRepo::class)->destroy($viewer);
85 $resp = $this->get('settings/audit');
86 $resp->assertSeeText("[ID: {$viewer->id}] Deleted User");
89 public function test_filters_by_key()
91 $this->actingAs($this->getAdmin());
92 $page = Page::query()->first();
93 $this->activityService->add(ActivityType::PAGE_CREATE, $page);
95 $resp = $this->get('settings/audit');
96 $resp->assertSeeText($page->name);
98 $resp = $this->get('settings/audit?event=page_delete');
99 $resp->assertDontSeeText($page->name);
102 public function test_date_filters()
104 $this->actingAs($this->getAdmin());
105 $page = Page::query()->first();
106 $this->activityService->add(ActivityType::PAGE_CREATE, $page);
108 $yesterday = (Carbon::now()->subDay()->format('Y-m-d'));
109 $tomorrow = (Carbon::now()->addDay()->format('Y-m-d'));
111 $resp = $this->get('settings/audit?date_from=' . $yesterday);
112 $resp->assertSeeText($page->name);
114 $resp = $this->get('settings/audit?date_from=' . $tomorrow);
115 $resp->assertDontSeeText($page->name);
117 $resp = $this->get('settings/audit?date_to=' . $tomorrow);
118 $resp->assertSeeText($page->name);
120 $resp = $this->get('settings/audit?date_to=' . $yesterday);
121 $resp->assertDontSeeText($page->name);
124 public function test_user_filter()
126 $admin = $this->getAdmin();
127 $editor = $this->getEditor();
128 $this->actingAs($admin);
129 $page = Page::query()->first();
130 $this->activityService->add(ActivityType::PAGE_CREATE, $page);
132 $this->actingAs($editor);
133 $chapter = Chapter::query()->first();
134 $this->activityService->add(ActivityType::CHAPTER_UPDATE, $chapter);
136 $resp = $this->actingAs($admin)->get('settings/audit?user=' . $admin->id);
137 $resp->assertSeeText($page->name);
138 $resp->assertDontSeeText($chapter->name);
140 $resp = $this->actingAs($admin)->get('settings/audit?user=' . $editor->id);
141 $resp->assertSeeText($chapter->name);
142 $resp->assertDontSeeText($page->name);
145 public function test_ip_address_logged_and_visible()
147 config()->set('app.proxies', '*');
148 $editor = $this->getEditor();
149 /** @var Page $page */
150 $page = Page::query()->first();
152 $this->actingAs($editor)->put($page->getUrl(), [
153 'name' => 'Updated page',
154 'html' => '<p>Updated content</p>',
156 'X-Forwarded-For' => '192.123.45.1',
157 ])->assertRedirect($page->refresh()->getUrl());
159 $this->assertDatabaseHas('activities', [
160 'type' => ActivityType::PAGE_UPDATE,
161 'ip' => '192.123.45.1',
162 'user_id' => $editor->id,
163 'entity_id' => $page->id,
166 $resp = $this->asAdmin()->get('/settings/audit');
167 $resp->assertSee('192.123.45.1');
170 public function test_ip_address_is_searchable()
172 config()->set('app.proxies', '*');
173 $editor = $this->getEditor();
174 /** @var Page $page */
175 $page = Page::query()->first();
177 $this->actingAs($editor)->put($page->getUrl(), [
178 'name' => 'Updated page',
179 'html' => '<p>Updated content</p>',
181 'X-Forwarded-For' => '192.123.45.1',
182 ])->assertRedirect($page->refresh()->getUrl());
184 $this->actingAs($editor)->put($page->getUrl(), [
185 'name' => 'Updated page',
186 'html' => '<p>Updated content</p>',
188 'X-Forwarded-For' => '192.122.45.1',
189 ])->assertRedirect($page->refresh()->getUrl());
191 $resp = $this->asAdmin()->get('/settings/audit?&ip=192.123');
192 $resp->assertSee('192.123.45.1');
193 $resp->assertDontSee('192.122.45.1');
196 public function test_ip_address_not_logged_in_demo_mode()
198 config()->set('app.proxies', '*');
199 config()->set('app.env', 'demo');
200 $editor = $this->getEditor();
201 /** @var Page $page */
202 $page = Page::query()->first();
204 $this->actingAs($editor)->put($page->getUrl(), [
205 'name' => 'Updated page',
206 'html' => '<p>Updated content</p>',
208 'X-Forwarded-For' => '192.123.45.1',
209 'REMOTE_ADDR' => '192.123.45.2',
210 ])->assertRedirect($page->refresh()->getUrl());
212 $this->assertDatabaseHas('activities', [
213 'type' => ActivityType::PAGE_UPDATE,
215 'user_id' => $editor->id,
216 'entity_id' => $page->id,
220 public function test_ip_address_respects_precision_setting()
222 config()->set('app.proxies', '*');
223 config()->set('app.ip_address_precision', 2);
224 $editor = $this->getEditor();
225 /** @var Page $page */
226 $page = Page::query()->first();
228 $this->actingAs($editor)->put($page->getUrl(), [
229 'name' => 'Updated page',
230 'html' => '<p>Updated content</p>',
232 'X-Forwarded-For' => '192.123.45.1',
233 ])->assertRedirect($page->refresh()->getUrl());
235 $this->assertDatabaseHas('activities', [
236 'type' => ActivityType::PAGE_UPDATE,
237 'ip' => '192.123.x.x',
238 'user_id' => $editor->id,
239 'entity_id' => $page->id,