FixUsernames.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. <?php
  2. namespace App\Console\Commands;
  3. use Illuminate\Console\Command;
  4. use App\{Profile, User};
  5. use DB;
  6. use App\Util\Lexer\RestrictedNames;
  7. class FixUsernames extends Command
  8. {
  9. /**
  10. * The name and signature of the console command.
  11. *
  12. * @var string
  13. */
  14. protected $signature = 'fix:usernames';
  15. /**
  16. * The console command description.
  17. *
  18. * @var string
  19. */
  20. protected $description = 'Fix invalid usernames';
  21. /**
  22. * Create a new command instance.
  23. *
  24. * @return void
  25. */
  26. public function __construct()
  27. {
  28. parent::__construct();
  29. }
  30. /**
  31. * Execute the console command.
  32. *
  33. * @return mixed
  34. */
  35. public function handle()
  36. {
  37. $this->line(' ');
  38. $this->info('Collecting data ...');
  39. $this->line(' ');
  40. $this->restrictedCheck();
  41. }
  42. protected function restrictedCheck()
  43. {
  44. $affected = collect([]);
  45. $restricted = RestrictedNames::get();
  46. $users = User::chunk(100, function($users) use($affected, $restricted) {
  47. foreach($users as $user) {
  48. if($user->is_admin || $user->status == 'deleted') {
  49. continue;
  50. }
  51. if(in_array(strtolower($user->username), array_map('strtolower', $restricted))) {
  52. $affected->push($user);
  53. }
  54. $val = str_replace(['-', '_', '.'], '', $user->username);
  55. if(!ctype_alnum($val)) {
  56. $this->info('Found invalid username: ' . $user->username);
  57. $affected->push($user);
  58. }
  59. }
  60. });
  61. if($affected->count() > 0) {
  62. $this->info('Found: ' . $affected->count() . ' affected usernames');
  63. $opts = [
  64. 'Random replace (assigns random username)',
  65. 'Best try replace (assigns alpha numeric username)',
  66. 'Manual replace (manually set username)',
  67. 'Skip (do not replace. Use at your own risk)'
  68. ];
  69. foreach($affected as $u) {
  70. $old = $u->username;
  71. $this->info("Found user: {$old}");
  72. $opt = $this->choice('Select fix method:', $opts, 3);
  73. switch ($opt) {
  74. case $opts[0]:
  75. $new = "user_" . str_random(6);
  76. $this->info('New username: ' . $new);
  77. break;
  78. case $opts[1]:
  79. $new = filter_var($old, FILTER_SANITIZE_STRING|FILTER_FLAG_STRIP_LOW);
  80. if(strlen($new) < 6) {
  81. $new = $new . '_' . str_random(4);
  82. }
  83. $this->info('New username: ' . $new);
  84. break;
  85. case $opts[2]:
  86. $new = $this->ask('Enter new username:');
  87. $this->info('New username: ' . $new);
  88. break;
  89. case $opts[3]:
  90. $new = false;
  91. break;
  92. default:
  93. $new = "user_" . str_random(6);
  94. break;
  95. }
  96. if($new) {
  97. DB::transaction(function() use($u, $new) {
  98. $profile = $u->profile;
  99. $profile->username = $new;
  100. $u->username = $new;
  101. $u->save();
  102. $profile->save();
  103. });
  104. }
  105. $this->info('Selected: ' . $opt);
  106. }
  107. $this->info('Fixed ' . $affected->count() . ' usernames!');
  108. } else {
  109. $this->info('No restricted usernames found!');
  110. }
  111. $this->line(' ');
  112. $this->versionZeroTenNineFix();
  113. }
  114. protected function versionZeroTenNineFix()
  115. {
  116. $profiles = Profile::whereNotNull('domain')
  117. ->whereNull('private_key')
  118. ->where('username', 'not like', '@%@%')
  119. ->get();
  120. $count = $profiles->count();
  121. if($count > 0) {
  122. $this->info("Found {$count} remote usernames to fix ...");
  123. $this->line(' ');
  124. } else {
  125. $this->info('No remote fixes found!');
  126. $this->line(' ');
  127. return;
  128. }
  129. foreach($profiles as $p) {
  130. $this->info("Fixed $p->username => $p->webfinger");
  131. $p->username = $p->webfinger ?? "@{$p->username}@{$p->domain}";
  132. if(Profile::whereUsername($p->username)->exists()) {
  133. return;
  134. }
  135. $p->save();
  136. }
  137. if($count > 0) {
  138. $this->line(' ');
  139. }
  140. }
  141. }