forked from ProcessMaker/processmaker
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSanitizeUsernames.php
More file actions
142 lines (123 loc) · 4.49 KB
/
SanitizeUsernames.php
File metadata and controls
142 lines (123 loc) · 4.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<?php
namespace ProcessMaker\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Validator;
use ProcessMaker\Models\User;
class SanitizeUsernames implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $users;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Collection $users)
{
$this->users = $users;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
foreach ($this->users as $index => $user) {
// Store the pre-update username for comparison
$pre_update_username = $user->username;
$updated_username = static::filterAndValidateUsername($user->username, $user->id);
// If it's the same, then it was already valid
if ($pre_update_username === $updated_username) {
continue;
}
// Set the valid username to the user
$updated = DB::table('users')->where('id', $user->id)->update([
'username' => $updated_username,
]);
// Log the result and continue
if ($updated) {
// Search through comments and replace the previous username
// with the recently updated, valid username
self::findAndReplaceUsernameInComments($pre_update_username, $updated_username);
// Log the the changes
logger()->info("Username Updated From ({$pre_update_username}) to ({$updated_username})", [
'user_id' => $user->id,
'updated_from_username' => $pre_update_username,
'updated_to_username' => $updated_username,
]);
}
}
}
/**
* Search through existing comments for usernames in plain text and swap out
* the username with disallowed characters with the filtered/validated one
*
* @param string $previous_username
* @param string $new_username
*
* @return void
*/
public static function findAndReplaceUsernameInComments(string $previous_username, string $new_username)
{
if (!self::packageCommentsInstalled()) {
return;
}
$comments_with_username = DB::table('comments')
->where('body', 'like', "%@{$previous_username}%")
->select('id', 'body')
->orderBy('id')
->get();
foreach ($comments_with_username as $comment) {
DB::table('comments')->where('id', $comment->id)->update([
'body' => str_replace("@{$previous_username} ", "@$new_username ", $comment->body),
]);
}
}
/**
* Provide the user id or User model and receive a validated username string
*
* @param string $username
* @param int $id
*
* @return string
* @throws \Exception
*/
public static function filterAndValidateUsername(string $username, int $id): string
{
$i = 0;
$generator = static function () use ($username, &$i): string {
if (blank($username = mb_ereg_replace('[^\p{L}\p{N}\-_\.\@\+\s]', '', $username))) {
$username = 'user_' . random_bytes(4);
}
return $i++ !== 0 ? $username . $i : $username;
};
do {
// Ensure uniqueness for the username
$unique_username_query = DB::table('users')
->where('username', $username = $generator())
->where('id', '!=', $id)
->orderBy('id');
} while ($unique_username_query->exists());
// Once we know it's a unique, valid
// username, send it back
return $username;
}
/**
* ProcessMaker-specific package comments is installed or now
*
* @return bool
*/
public static function packageCommentsInstalled(): bool
{
return File::exists(base_path('vendor/processmaker/package-comments'));
}
}