Issues (23)

Security Analysis    no vulnerabilities found

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

app/Http/Controllers/UserController.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace SET\Http\Controllers;
4
5
use Carbon\Carbon;
6
use Illuminate\Support\Facades\DB;
7
use Illuminate\Support\Facades\File;
8
use Illuminate\Support\Facades\Gate;
9
use Illuminate\Support\Facades\Storage;
10
use Krucas\Notification\Facades\Notification;
11
use SET\Duty;
12
use SET\Group;
13
use SET\Handlers\Excel\JpasImport;
14
use SET\Http\Requests\StoreUserRequest;
15
use SET\Http\Requests\UpdateUserRequest;
16
use SET\Setting;
17
use SET\Training;
18
use SET\User;
19
20
/**
21
 * Class UserController.
22
 */
23
class UserController extends Controller
24
{
25
    /**
26
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
27
     */
28
    public function index($userStatus = null)
29
    {
30
        $this->authorize('view');
31
32
        session(['userStatus' => $userStatus]);
33
        if ($userStatus) {
34
            $users = User::with([
35 View Code Duplication
                'assignedTrainings' => function ($q) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
36
                    $q->whereNull('completed_date')
37
                        ->whereBetween('due_date', [Carbon::now()->subYear(), Carbon::now()->addWeeks(4)]);
38
                },
39
                'trainings',
40
            ])
41
                ->skipSystem()
42
                ->where('status', $userStatus)
43
                ->orderBy('last_name')->get();
44
        } else {
45
            $users = User::with([
46 View Code Duplication
                'assignedTrainings' => function ($q) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
47
                    $q->whereNull('completed_date')
48
                        ->whereBetween('due_date', [Carbon::now()->subYear(), Carbon::now()->addWeeks(4)]);
49
                },
50
                'trainings',
51
            ])
52
            ->skipSystem()
53
            ->orderBy('last_name')->get();
54
        }
55
56
        return view('user.index', compact('users', 'userStatus'));
57
    }
58
59 View Code Duplication
    public function create()
60
    {
61
        $this->authorize('edit');
62
63
        $supervisors = User::skipSystem()->active()->orderBy('last_name')->get()->pluck('userFullName', 'id')->toArray();
64
        $groups = Group::all();
65
66
        return view('user.create', compact('supervisors', 'groups'));
67
    }
68
69
    /**
70
     * @param StoreUserRequest $request
71
     *
72
     * @return \Illuminate\Http\RedirectResponse
73
     */
74
    public function store(StoreUserRequest $request)
75
    {
76
        $data = $request->all();
77
        $data['status'] = 'active';
78
        $user = User::create($data);
79
80
        $user->accessTokens()->create($data['accessTokens']);
81
82
        if (array_key_exists('groups', $data)) {
83
            settype($data['groups'], 'array');
84
            $user->groups()->sync($data['groups']);
85
        }
86
87
        return redirect()->action('UserController@index');
88
    }
89
90
    /**
91
     * @param $userId
92
     *
93
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
94
     */
95
    public function show($userId, $sectionId = null)
96
    {
97
        $user = User::with(['subordinates' => function ($query) {
98
            $query->active();
99
        },
100
                            'groups', 'duties', 'attachments',
101
                            'visits', 'notes.author', 'notes.attachments',
102
                            'travels.author', 'travels.attachments',
103
                            'accessTokens'])
104
                    ->findOrFail($userId);
105
106
        //Make sure the user can't access other people's pages.
107
        $this->authorize('show_user', $user);
108
109
        $user['clearance'] = $this->spellOutClearance($user['clearance']);
110
        $user['access_level'] = $this->spellOutClearance($user['access_level']);
111
112
        $trainings = $this->getUserTrainings($user, $sectionId);
113
        $user_training_types = $this->getUserTrainingTypes($trainings);
114
        $training_user_types = $user_training_types[0]; // List of the user's training types
115
        $training_blocks = $user_training_types[1]; // List of training block titles for user
116
117
        $activityLog = [];
118
        if (Gate::allows('view')) {
119
            $activityLog = $user->getUserLog($user);
120
        }
121
        
122
        $this->previousAndNextUsers($user, $previous, $next);
123
124
        //This mess is just so that we can output the Security Check list or show none. Mainly just to show none.
125
        $duties = Duty::whereHas('users', function ($q) use ($userId) {
126
            $q->where('id', $userId);
127
        })->orWhereHas('groups.users', function ($q) use ($userId) {
128
            $q->where('id', $userId);
129
        })->get();
130
131
        return view('user.show', compact('user', 'duties', 'previous', 'next',
132
            'trainings', 'activityLog', 'training_blocks', 'training_user_types'));
133
    }
134
135 View Code Duplication
    public function edit(User $user)
136
    {
137
        $this->authorize('edit');
138
139
        $supervisors = User::skipSystem()->active()->orderBy('last_name')->get()->pluck('userFullName', 'id')->toArray();
140
        $groups = Group::all();
141
142
        return view('user.edit', compact('user', 'supervisors', 'groups'));
143
    }
144
145
    public function update(UpdateUserRequest $request, User $user)
146
    {
147
        $this->authorize('edit');
148
        $data = $request->all();
149
150
        //$data['destroyed_date'] = $user->getDestroyDate($data['status']);
151
152
        // If continuous evaluation is false, there should be no date associated with it
153
        if ($data['cont_eval'] == 0)
154
            $data['cont_eval_date'] = null;
155
156
157
        $user->update($data);
158
159
        //Handle user groups
160
        if (!array_key_exists('groups', $data)) {
161
            $data['groups'] = [];
162
        }
163
        $user->groups()->sync($data['groups']);
164
        
165
        $user->accessTokens()->updateOrCreate(['user_id' => $user->id], $data['accessTokens']);
166
167
        //Handled closed area access (MUST come AFTER syncing groups).
168
        if (array_key_exists('access', $data)) {
169
            foreach ($data['access'] as $group_id => $accessLevel) {
170
                $user->groups()->updateExistingPivot($group_id, ['access' => $accessLevel]);
171
            }
172
        }
173
174
        return redirect()->action('UserController@show', $user->id);
175
    }
176
177
    /**
178
     * @param $userId
179
     *
180
     * @return string
181
     */
182
    public function destroy($userId)
183
    {
184
        $this->authorize('edit');
185
186
        Storage::deleteDirectory('user_'.$userId);
187
        User::findOrFail($userId)->delete();
188
189
        return 'success';
190
    }
191
192
    /**
193
     * Get all the scheduled trainings for the user and either the most recent completed
194
     * trainings or all completed trainings for the desired training type based on data values.
195
     *
196
     * @param $user
197
     * @param $data
198
     *
199
     * @return array trainings
200
     */
201
    private function getUserTrainings($user, $sectionId)
202
    {
203
        // Get the scheduled trainings and the completed trainings separately, so it will be easier to get either
204
        // the full list of completed trainings or the most recently completed training for each training type.
205
        // Fetch scheduled trainings only if the user is active.
206
        if ($user->status == 'active') {
207
            $scheduledTrainings = $user->assignedTrainings()->with('author', 'training.attachments', 'attachments')
208
                                ->whereNull('completed_date');
209
        }
210
211
        // Now fetch the recently completed training records for the specified  user
212
        $recentCompletedTrainings = DB::table('training_user as t1')
213
                                    ->where('id', function ($query) use ($user) {
214
                                        $query->from('training_user as t2')
215
                                              ->selectRaw('t2.id')
216
                                              ->where('t2.user_id', '=', $user->id)
217
                                              ->whereRaw('t1.training_id = t2.training_id')
218
                                              ->whereNotNull('completed_date')
219
                                              ->orderBy('completed_date', 'DESC')->limit(1);
220
                                    });
221
222
        // If the user clicks the showAll button for a particular type of training,
223
        // show all the trainings completed by that user for that training type.
224
        if ($sectionId != null) {
225
            $selectedTraining = Training::trainingByType($sectionId)->get()->pluck('id')->toArray();
0 ignored issues
show
The method trainingByType() does not exist on SET\Training. Did you maybe mean scopeTrainingByType()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
226
227
            $completedTrainings = $user->assignedTrainings()->with('author', 'training.attachments', 'attachments')
228
                                    ->whereIn('training_id', $selectedTraining)->whereNotNull('completed_date');
229
230
            if (isset($scheduledTrainings)) {
231
                $trainings = $scheduledTrainings->union($completedTrainings)->union($recentCompletedTrainings)
232
                                        ->orderBy('completed_date', 'DESC')->get();
233
            } else {
234
                $trainings = $completedTrainings->union($recentCompletedTrainings)
235
                                ->orderBy('completed_date', 'DESC')->get();
236
            }
237
        } else {
238
            if (isset($scheduledTrainings)) {
239
                $trainings = $scheduledTrainings->union($recentCompletedTrainings)
240
                                ->orderBy('completed_date', 'DESC')->get();
241
            } else {
242
                $rcTrainingIds = $recentCompletedTrainings->get()->pluck('training_id')->toArray();
243
                $trainings = $user->assignedTrainings()->with('author', 'training.attachments', 'attachments')
244
                                    ->whereIn('training_id', $rcTrainingIds)
245
                                    ->orderBy('completed_date', 'DESC')->get();
246
            }
247
        }
248
249
        return $trainings;
250
    }
251
252
    /**
253
     * @param $trainings[]
254
     * From the User's trainings, a list of the training types is determined and
255
     * a list of the training block titles is determined.
256
     *
257
     * @return user_training_types[], training_block_titles[]
0 ignored issues
show
The doc-type user_training_types[], could not be parsed: Expected "|" or "end of type", but got "," at position 21. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
258
     */
259
    public function getUserTrainingTypes($trainings = [])
260
    {
261
        $training_block_titles = $user_training_types = [];
262
        foreach ($trainings as $trainingUser) {
263
            if (is_null($trainingUser->completed_date)) {
264
                $training_block_titles['AAA'] = 'Scheduled';
265
                $user_training_types[$trainingUser->id] = 'Scheduled';
266
            } elseif ($trainingUser->Training->trainingType) {
267
                $typeName = $trainingUser->Training->trainingType->name;
268
                $training_block_titles[$typeName] = $typeName;
269
                $user_training_types[$trainingUser->id] = $typeName;
270
            } else { // No training type
271
                $training_block_titles['999'] = 'Miscellaneous';
272
                $user_training_types[$trainingUser->id] = 'Miscellaneous';
273
            }
274
        }
275
        ksort($training_block_titles);  // Order by key
276
        return [$user_training_types, $training_block_titles];
277
    }
278
279
    /**
280
     * Process our JPAS import. Once that has been handled, we pass the file, changes,
281
     * unique/unmapped users & a user list to the user.import view.
282
     * That way we keep all this data for the resolve phase.
283
     *
284
     * @param JpasImport $import
285
     *
286
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
287
     */
288
    public function import(JpasImport $import)
289
    {
290
        $results = $import->handleImport();
291
        $uploadedFile = $import->getFile('file');
292
293
        $changes = $results['changes'];
294
        $unique = $results['unique'];
295
296
        $userList = User::orderBy('last_name')->get()->pluck('UserFullName', 'id')->toArray();
297
298
        return view('user.import', compact('unique', 'changes', 'userList', 'uploadedFile'));
299
    }
300
301
    /**
302
     * @param JpasImport $import
303
     *
304
     * @return \Illuminate\Http\RedirectResponse
305
     */
306
    public function resolveImport(JpasImport $import)
307
    {
308
        $import->handleImport();
309
310
        Notification::container()->success('Import Complete');
311
312
        File::delete($import->getFile('file'));
313
314
        return redirect()->action('HomeController@index');
315
    }
316
317
    /**
318
     * Generate the grab the previous and next user if our users are sorted alphabetically.
319
     *
320
     * @param $user
321
     * @param $previous
322
     * @param $next
323
     */
324
    private function previousAndNextUsers($user, &$previous, &$next)
325
    {  
326
          //Build the previous/next user that are in alphabetical order, with respect to admin's preference
327
        if(Setting::get('full_name_format') == 'first_last'){
328
            $users = User::skipSystem()->userStatus()->orderBy( 'first_name ')->orderBy( 'last_name' )->get();
329
        } else{
330
            $users = User::skipSystem()->userStatus()->orderBy( 'last_name' )->orderBy( 'first_name' )->get();
331
        }
332
        
333
        
334
        $previous = null; // set to null by default in case we are at the start of the list.
335
        while ($users->first()->id != $user->id) {
336
            $previous = $users->shift()->id;
337
        }
338
        //check if we have a record aft the current user. If not, then we are at the end.
339
        if ($users->count() > 1) {
340
            $users->shift();
341
            $next = $users->shift()->id;
342
        } else {
343
            $next = null;
344
        }
345
    }
346
347
    /**
348
     * @param $clearance
349
     *
350
     * @return mixed
351
     */
352
    private function spellOutClearance($clearance)
353
    {
354
        //fully spell out user's clearance.
355
        switch ($clearance) {
356
            case 'S':
357
                $clearance = 'Secret';
358
                break;
359
            case 'TS':
360
                $clearance = 'Top Secret';
361
                break;
362
            case 'Int S':
363
                $clearance = 'Interim Secret';
364
                break;
365
            case 'Int TS':
366
                $clearance = 'Interim Top Secret';
367
                break;
368
        }
369
370
        return $clearance;
371
    }
372
}
373