1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Dyrynda\Artisan\Console\Commands; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use SplFileInfo; |
7
|
|
|
use Illuminate\Console\Command; |
8
|
|
|
use Illuminate\Support\Facades\Password; |
9
|
|
|
use Dyrynda\Artisan\Exceptions\MakeUserException; |
10
|
|
|
use Dyrynda\Artisan\Exceptions\ImportFileException; |
11
|
|
|
use Dyrynda\Artisan\BulkImport\BulkImportFileHandler; |
12
|
|
|
|
13
|
|
|
class MakeUser extends Command |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* The name and signature of the console command. |
17
|
|
|
* |
18
|
|
|
* @var string |
19
|
|
|
*/ |
20
|
|
|
protected $signature = 'make:user |
21
|
|
|
{--email= : Set the email for the new user} |
22
|
|
|
{--name= : Set the name for the new user} |
23
|
|
|
{--password= : The password to set for the new user} |
24
|
|
|
{--send-reset : Send a password reset email for the new user} |
25
|
|
|
{--fields= : Additional database fields to set on the user} |
26
|
|
|
{--force : Create the user model circumventing guarded fields} |
27
|
|
|
{--import-file= : Relative path and filename for a file to import users from. File name MUST contain the extension representing the type of file (Ex: ./path/to/file.csv)} |
28
|
|
|
'; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* The console command description. |
32
|
|
|
* |
33
|
|
|
* @var string |
34
|
|
|
*/ |
35
|
|
|
protected $description = 'Create new application users'; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Execute the console command. |
39
|
|
|
* |
40
|
|
|
* Handle creation of the new application user. |
41
|
|
|
* |
42
|
|
|
* @return void |
43
|
|
|
*/ |
44
|
|
|
public function handle() |
45
|
|
|
{ |
46
|
|
|
try { |
47
|
|
|
$bulkImportFile = $this->option('import-file') ? $this->fileHandlerFactory($this->option('import-file')) : null; |
|
|
|
|
48
|
|
|
|
49
|
|
|
$modelCommand = $this->option('force') ? 'forceCreate' : 'create'; |
50
|
|
|
|
51
|
|
|
if (! is_null($bulkImportFile)) { |
52
|
|
|
$dataToProcess = $bulkImportFile->getData(); |
53
|
|
|
|
54
|
|
|
$sendReset = false; |
55
|
|
|
|
56
|
|
|
if (! in_array('password', array_keys($dataToProcess[0]))) { |
57
|
|
|
$dataToProcess = $this->setDefaultPassword($dataToProcess); |
58
|
|
|
$sendReset = true; |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
} else { |
62
|
|
|
$email = $this->option('email'); |
63
|
|
|
$name = $this->option('name') ?: ''; |
64
|
|
|
$password = bcrypt($this->option('password') ?: str_random(32)); |
65
|
|
|
$sendReset = ! $this->option('password') || $this->option('send-reset'); |
66
|
|
|
|
67
|
|
|
$dataToProcess[0] = [ |
|
|
|
|
68
|
|
|
'email' => $email, |
69
|
|
|
'name' => $name, |
70
|
|
|
'password' => $password, |
71
|
|
|
]; |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
foreach ($dataToProcess as $dataRow) { |
75
|
|
|
|
76
|
|
|
$email = $dataRow['email'] ?? null; |
77
|
|
|
|
78
|
|
|
app('db')->beginTransaction(); |
79
|
|
|
|
80
|
|
|
$this->validateEmail($email); |
81
|
|
|
|
82
|
|
|
app(config('auth.providers.users.model'))->{$modelCommand}(array_merge( |
83
|
|
|
$dataRow, |
84
|
|
|
$bulkImportFile ? [] : $this->additionalFields() |
85
|
|
|
)); |
86
|
|
|
|
87
|
|
|
if ($sendReset) { |
88
|
|
|
Password::sendResetLink(compact('email')); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
app('db')->commit(); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
$createdMessage = $bulkImportFile |
95
|
|
|
? "Created " . count($dataToProcess) . " user(s)." |
96
|
|
|
: "Created new user for email {$email}."; |
|
|
|
|
97
|
|
|
|
98
|
|
|
$passwordResetMessage = $bulkImportFile |
99
|
|
|
? "Sent password reset emails." |
100
|
|
|
: "Sent password reset email to {$email}."; |
101
|
|
|
|
102
|
|
|
$this->info($createdMessage); |
103
|
|
|
|
104
|
|
|
if ($sendReset) { |
105
|
|
|
$this->info($passwordResetMessage); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
} catch (Exception $e) { |
109
|
|
|
$this->error($e->getMessage()); |
110
|
|
|
|
111
|
|
|
$this->error('The user(s) were not created'); |
112
|
|
|
|
113
|
|
|
app('db')->rollBack(); |
114
|
|
|
} |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Determine if the given email address already exists. |
119
|
|
|
* |
120
|
|
|
* @param string $email |
121
|
|
|
* @return void |
122
|
|
|
* |
123
|
|
|
* @throws \Dyrynda\Artisan\Exceptions\MakeUserException |
124
|
|
|
*/ |
125
|
|
|
private function validateEmail($email) |
126
|
|
|
{ |
127
|
|
|
if (! filter_var($email, FILTER_VALIDATE_EMAIL)) { |
128
|
|
|
throw MakeUserException::invalidEmail($email); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
if (app(config('auth.providers.users.model'))->where('email', $email)->exists()) { |
132
|
|
|
throw MakeUserException::emailExists($email); |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* Return any additional database fields passed by the --fields option. |
138
|
|
|
* |
139
|
|
|
* @return array |
140
|
|
|
*/ |
141
|
|
|
private function additionalFields() |
142
|
|
|
{ |
143
|
|
|
if (! $this->option('fields')) { |
144
|
|
|
return []; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
return collect(explode(',', $this->option('fields')))->mapWithKeys(function ($field) { |
148
|
|
|
list($column, $value) = explode(':', $field); |
149
|
|
|
|
150
|
|
|
return [trim($column) => $this->normaliseValue($value)]; |
151
|
|
|
})->toArray(); |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Normalise the given (database) field input value. |
156
|
|
|
* |
157
|
|
|
* @param mixed $value |
158
|
|
|
* @return mixed |
159
|
|
|
*/ |
160
|
|
|
private function normaliseValue($value) |
161
|
|
|
{ |
162
|
|
|
if ($value == 'null') { |
163
|
|
|
return; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
View Code Duplication |
if (in_array($value, [1, 'true', true], true)) { |
167
|
|
|
return true; |
168
|
|
|
} |
169
|
|
|
|
170
|
|
View Code Duplication |
if (in_array($value, [0, 'false', false], true)) { |
171
|
|
|
return false; |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
return trim($value); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* Create file handler objects |
179
|
|
|
* |
180
|
|
|
* @param string $path |
181
|
|
|
* @return BulkImportFileHandler |
182
|
|
|
* |
183
|
|
|
* @throws \Dyrynda\Artisan\Exceptions\ImportFileException |
184
|
|
|
*/ |
185
|
|
|
private function fileHandlerFactory($path) : BulkImportFileHandler |
186
|
|
|
{ |
187
|
|
|
if (! strpos($path, '.')) { |
188
|
|
|
throw ImportFileException::noExtension(); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
$file = new SplFileInfo($path); |
192
|
|
|
|
193
|
|
|
if (! class_exists($class = '\\Dyrynda\\Artisan\\BulkImport\\Handlers\\' . studly_case($file->getExtension()))) { |
194
|
|
|
throw ImportFileException::unsupported($file->getExtension()); |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
return new $class($path); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* Add default password to data |
202
|
|
|
* |
203
|
|
|
* @param array $data |
204
|
|
|
* @return array |
205
|
|
|
* |
206
|
|
|
*/ |
207
|
|
|
private function setDefaultPassword($data) |
208
|
|
|
{ |
209
|
|
|
return collect($data)->map(function($row){ |
210
|
|
|
return array_merge(['password' => str_random(32)], $row); |
211
|
|
|
})->all(); |
212
|
|
|
} |
213
|
|
|
} |
214
|
|
|
|
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.