Skip to content

Commit 33ab762

Browse files
committed
Add support for tracking more SFTP specific events
1 parent 2e01891 commit 33ab762

File tree

5 files changed

+38
-11
lines changed

5 files changed

+38
-11
lines changed

app/Http/Controllers/Api/Remote/SftpAuthenticationController.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Pterodactyl\Models\User;
77
use Pterodactyl\Models\Server;
88
use Illuminate\Http\JsonResponse;
9+
use Pterodactyl\Facades\Activity;
910
use Pterodactyl\Models\Permission;
1011
use phpseclib3\Crypt\PublicKeyLoader;
1112
use Pterodactyl\Http\Controllers\Controller;
@@ -51,6 +52,8 @@ public function __invoke(SftpAuthenticationFormRequest $request): JsonResponse
5152

5253
if ($request->input('type') !== 'public_key') {
5354
if (!password_verify($request->input('password'), $user->password)) {
55+
Activity::event('auth:sftp.fail')->property('method', 'password')->subject($user)->log();
56+
5457
$this->reject($request);
5558
}
5659
} else {
@@ -62,13 +65,29 @@ public function __invoke(SftpAuthenticationFormRequest $request): JsonResponse
6265
}
6366

6467
if (!$key || !$user->sshKeys()->where('fingerprint', $key->getFingerprint('sha256'))->exists()) {
68+
// We don't log here because of the way the SFTP system works. This endpoint
69+
// will get hit for every key the user provides, which could be 4 or 5. That is
70+
// a lot of unnecessary log noise.
71+
//
72+
// For now, we'll only log failures due to a bad password as those are not likely
73+
// to occur more than once in a session for the user, and are more likely to be of
74+
// value to the end user.
6575
$this->reject($request, is_null($key));
6676
}
6777
}
6878

6979
$this->validateSftpAccess($user, $server);
7080

81+
Activity::event('auth:sftp.success')->actor($user)
82+
->subject($user)
83+
->property(array_filter([
84+
'method' => isset($key) ? 'ssh_key' : 'password',
85+
'fingerprint' => isset($key) ? 'SHA256:' . $key->getFingerprint('sha256') : null,
86+
]))
87+
->log();
88+
7189
return new JsonResponse([
90+
'user' => $user->uuid,
7291
'server' => $server->uuid,
7392
'permissions' => $this->permissions->handle($server, $user),
7493
]);
@@ -136,6 +155,8 @@ protected function validateSftpAccess(User $user, Server $server): void
136155
$permissions = $this->permissions->handle($server, $user);
137156

138157
if (!in_array(Permission::ACTION_FILE_SFTP, $permissions)) {
158+
Activity::event('server:sftp.denied')->actor($user)->subject($server)->log();
159+
139160
throw new HttpForbiddenException('You do not have permission to access SFTP for this server.');
140161
}
141162
}

app/Transformers/Api/Client/ActivityLogTransformer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ protected function hasAdditionalMetadata(ActivityLog $model): bool
9292
$str = trans('activity.' . str_replace(':', '.', $model->event));
9393
preg_match_all('/:(?<key>[\w.-]+\w)(?:[^\w:]?|$)/', $str, $matches);
9494

95-
$exclude = array_merge($matches['key'], ['ip', 'useragent']);
95+
$exclude = array_merge($matches['key'], ['ip', 'useragent', 'using_sftp']);
9696
foreach ($model->properties->keys() as $key) {
9797
if (!in_array($key, $exclude, true)) {
9898
return true;

resources/lang/en/activity.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
'recovery-token' => 'Used two-factor recovery token',
1717
'token' => 'Solved two-factor challenge',
1818
'ip-blocked' => 'Blocked request from unlisted IP address for :identifier',
19+
'sftp' => [
20+
'success' => 'Logged in using SFTP',
21+
'fail' => 'Failed SFTP log in',
22+
],
1923
],
2024
'user' => [
2125
'account' => [
@@ -96,6 +100,9 @@
96100
'update' => 'Updated the ":action" task for the :name schedule',
97101
'delete' => 'Deleted a task for the :name schedule',
98102
],
103+
'sftp' => [
104+
'denied' => 'Blocked SFTP access due to permissions',
105+
],
99106
'settings' => [
100107
'rename' => 'Renamed the server from :old to :new',
101108
],

resources/scripts/components/elements/activity/ActivityLogEntry.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import Translate from '@/components/elements/Translate';
55
import { format, formatDistanceToNowStrict } from 'date-fns';
66
import { ActivityLog } from '@definitions/user';
77
import ActivityLogMetaButton from '@/components/elements/activity/ActivityLogMetaButton';
8-
import { TerminalIcon } from '@heroicons/react/solid';
8+
import { FolderOpenIcon, TerminalIcon } from '@heroicons/react/solid';
99
import classNames from 'classnames';
1010
import style from './style.module.css';
1111
import Avatar from '@/components/Avatar';
@@ -65,10 +65,13 @@ export default ({ activity, children }: Props) => {
6565
</Link>
6666
<div className={classNames(style.icons, 'group-hover:text-gray-300')}>
6767
{activity.isApi && (
68-
<Tooltip placement={'top'} content={'Performed using API Key'}>
69-
<span>
70-
<TerminalIcon />
71-
</span>
68+
<Tooltip placement={'top'} content={'Using API Key'}>
69+
<TerminalIcon />
70+
</Tooltip>
71+
)}
72+
{activity.properties.using_sftp && (
73+
<Tooltip placement={'top'} content={'Using SFTP'}>
74+
<FolderOpenIcon />
7275
</Tooltip>
7376
)}
7477
{children}

resources/scripts/components/elements/activity/style.module.css

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
.icons {
22
@apply flex space-x-1 mx-2 transition-colors duration-100 text-gray-400;
33

4-
& > span {
5-
@apply px-1 py-px cursor-pointer hover:text-gray-50;
6-
}
7-
84
& svg {
9-
@apply w-4 h-4;
5+
@apply px-1 py-px cursor-pointer hover:text-gray-50 h-5 w-auto;
106
}
117
}
128

0 commit comments

Comments
 (0)