Passed
Push — master ( 2416bc...c76436 )
by Hesham
13:00 queued 01:26
created

LumenerController::_setupErrorHandling()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
dl 0
loc 13
rs 10
c 0
b 0
f 0
cc 2
nc 1
nop 0
1
<?php
2
namespace Lumener\Controllers;
3
4
use Illuminate\Routing\Controller;
5
use Illuminate\Http\Request;
6
use Illuminate\Support\Facades\Artisan;
7
8
class LumenerController extends Controller
9
{
10
    protected $adminer;
11
    protected $adminer_object;
12
    protected $plugins_path;
13
    protected $allowed_dbs;
14
    protected $protected_dbs;
15
    protected $request;
16
17
    public function __construct(Request $request)
18
    {
19
        if (method_exists(\Route::class, 'hasMiddlewareGroup')
20
        && \Route::hasMiddlewareGroup('lumener')) {
21
            $this->middleware('lumener');
22
        }
23
        // LumenerServiceProvider::register holds the middleware register
24
        // so it does not need to be addeed manually.
25
        // User-defined middleware is handled during route definition for Lumen
26
        $this->allowed_dbs = config('lumener.security.allowed_db');
27
        $this->protected_dbs = config('lumener.security.protected_db');
28
        $this->adminer = LUMENER_STORAGE.'/adminer.php';
29
        $this->adminer_object = __DIR__.'/../logic/adminer_object.php';
30
        $this->plugins_path = LUMENER_STORAGE.'/plugins';
31
        $this->request = $request;
32
    }
33
34
    public function __call($method, $params)
35
    {
36
        if (strncasecmp($method, "get", 3) === 0) {
37
            $var = preg_replace_callback('/[A-Z]/', function ($c) {
38
                return '_'.strtolower($c[0]);
39
            }, lcfirst(substr($method, 3)));
40
            return $this->$var;
41
        }
42
    }
43
44
    public function index()
45
    {
46
        if ($this->request->cookie('adminer_logged_out')
47
            && config('lumener.logout_redirect')) {
48
            return redirect(config('lumener.logout_redirect'));
49
        }
50
        if (isset($_POST['logout'])) {
51
            $t = encrypt(time());
52
            $h = "Set-Cookie: adminer_logged_out={$t}; expires=".gmdate(
53
                "D, d M Y H:i:s",
54
                time() + config('lumener.logout_cooldown', 10)
55
            )." GMT; path=".preg_replace('~\?.*~', '', $_SERVER["REQUEST_URI"]);
56
            header($h);
57
        }
58
        if (file_exists($this->adminer)) {
59
            return $this->_runAdminer();
60
        } else {
61
            return '<div style="text-align:center;color: red;
62
                                margin-top: 200px;font-weight:bold;">
63
                      Adminer was NOT found.
64
                      Run <span style="color:lightgreen;background:black;
65
                                       padding: 5px;border: 5px dashed white;">
66
                                       php artisan lumener:update --force</span>
67
                                       to fix any issues.
68
                    </div>
69
            ';
70
        }
71
    }
72
73
    public function update()
74
    {
75
        Artisan::call('lumener:update');
76
        return nl2br(Artisan::output());
77
    }
78
79
    public function getResource()
80
    {
81
        $file = $this->request->get('file');
82
        $path = realpath(LUMENER_STORAGE."/{$file}");
83
        // Prevent risky file fetching
84
        // This check is very important, it's a major security risk to allow
85
        // Fetching files outside the LUMENER_STORAGE directory
86
        if (
87
            $path === false
88
            || strncmp($path, LUMENER_STORAGE, strlen(LUMENER_STORAGE)) !== 0
89
        ) {
90
            abort(403);
91
        }
92
        $type = $this->request->get('type', mime_content_type($path));
93
        return response()->download($path, $file, ["Content-Type"=>$type]);
94
    }
95
96
    public function isDBBlocked($db)
97
    {
98
        return
99
        (
100
            $this->allowed_dbs !== null
101
            && !in_array($db, $this->allowed_dbs)
102
        )
103
        ||
104
        (
105
            $this->protected_dbs !== null
106
            && in_array($db, $this->protected_dbs)
107
        );
108
    }
109
110
    private function _runAdminer()
111
    {
112
        $this->_handleAdminerAutoLogin();
113
114
        // Known Issues
115
        $this->_patchAdminerRequest();
116
117
        // Security Check
118
        $this->_verifyAdminerRequest();
119
120
        $content =
121
            $this->_runGetBuffer([$this->adminer_object, $this->adminer]);
122
123
        if (strpos($content, "<!DOCTYPE html>") === false) {
124
            die($content);
125
        }
126
        return $content;
127
    }
128
129
    private function _handleAdminerAutoLogin()
130
    {
131
        if (!isset($_GET['username']) && !isset($_POST['auth'])
132
            && config('lumener.auto_login')
133
            && !$this->request->cookie('adminer_logged_out')) {
134
            // Skip login screen
135
            $_GET['username'] =
136
                config('lumener.db.username', env("DB_USERNAME"));
137
            $_GET['db'] =
138
                config('lumener.db.database', env("DB_DATABASE"));
139
            // Password is set in the adminer extension
140
        }
141
    }
142
143
    private function _verifyAdminerRequest()
144
    {
145
        if ((isset($_GET['db']) && $_GET['db']
146
            && $this->isDBBlocked($_GET['db']))
147
        || (isset($_POST['auth']['db']) && $_POST['auth']['db']
148
            && $this->isDBBlocked($_POST['auth']['db']))) {
149
            abort(403);
150
        }
151
    }
152
153
    private function _patchAdminerRequest()
154
    {
155
        if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
156
            $_SERVER['HTTP_IF_MODIFIED_SINCE'] = null;
157
        }
158
    }
159
160
    private function _runGetBuffer($files, $allowed_errors=[E_WARNING])
161
    {
162
        // Prepare for unhandled errors
163
        $this->_setupErrorHandling();
164
        // Require files
165
        ob_implicit_flush(0);
166
        ob_start();
167
        try {
168
            foreach ($files as $file) {
169
                // require because include will not throw the adminer_exit error
170
                require($file);
171
            }
172
        } catch (\ErrorException $e) {
173
            if (config('lumener.debug')
174
            || !in_array($e->getSeverity(), $allowed_errors)) {
175
                throw $e;
176
            }
177
        }
178
        $this->_stopErrorHandling();
179
        $content = "";
180
        while ($level = ob_get_clean()) {
181
            $content = $level . $content;
182
        }
183
        return $content;
184
    }
185
186
    private function _setupErrorHandling()
187
    {
188
        set_error_handler(function ($err_severity, $err_msg, $err_file, $err_line) {
189
            // Check if suppressed with the @-operator
190
            if (0 === error_reporting()) {
191
                return false;
192
            }
193
            throw new \ErrorException(
194
                $err_msg,
195
                0,
196
                $err_severity,
197
                $err_file,
198
                $err_line
199
            );
200
        });
201
    }
202
203
    private function _stopErrorHandling()
204
    {
205
        set_error_handler(null);
206
    }
207
}
208