mirror of
https://github.com/danielbrendel/hortusfox-web.git
synced 2026-01-05 20:30:34 -06:00
Proxy authentication
This commit is contained in:
@@ -8,6 +8,19 @@
|
||||
* Command handler class
|
||||
*/
|
||||
class MigrationUpgrade implements Asatru\Commands\Command {
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function upgradeTo3dot5()
|
||||
{
|
||||
AppModel::raw('ALTER TABLE `' . AppModel::tableName() . '` ADD COLUMN IF NOT EXISTS auth_proxy_enable BOOLEAN NOT NULL DEFAULT 0');
|
||||
AppModel::raw('ALTER TABLE `' . AppModel::tableName() . '` ADD COLUMN IF NOT EXISTS auth_proxy_header_email VARCHAR(512) NULL');
|
||||
AppModel::raw('ALTER TABLE `' . AppModel::tableName() . '` ADD COLUMN IF NOT EXISTS auth_proxy_header_username VARCHAR(512) NULL');
|
||||
AppModel::raw('ALTER TABLE `' . AppModel::tableName() . '` ADD COLUMN IF NOT EXISTS auth_proxy_sign_up BOOLEAN NOT NULL DEFAULT 0');
|
||||
AppModel::raw('ALTER TABLE `' . AppModel::tableName() . '` ADD COLUMN IF NOT EXISTS auth_proxy_whitelist TEXT NULL');
|
||||
AppModel::raw('ALTER TABLE `' . AppModel::tableName() . '` ADD COLUMN IF NOT EXISTS auth_proxy_hide_logout BOOLEAN NOT NULL DEFAULT 0');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
|
||||
@@ -115,6 +115,7 @@ return [
|
||||
array('/admin/location/add', 'POST', 'admin@add_location'),
|
||||
array('/admin/location/update', 'POST', 'admin@update_location'),
|
||||
array('/admin/location/remove', 'ANY', 'admin@remove_location'),
|
||||
array('/admin/auth/proxy/save', 'POST', 'admin@save_proxy_auth_settings'),
|
||||
array('/admin/attribute/schema/add', 'POST', 'admin@add_attribute_schema'),
|
||||
array('/admin/attribute/schema/edit', 'POST', 'admin@edit_attribute_schema'),
|
||||
array('/admin/attribute/schema/remove', 'ANY', 'admin@remove_attribute_schema'),
|
||||
|
||||
@@ -26,6 +26,15 @@ class BaseController extends Asatru\Controller\Controller {
|
||||
app_mail_config();
|
||||
app_set_timezone();
|
||||
|
||||
if (app('auth_proxy_enable')) {
|
||||
try {
|
||||
UserModel::performProxyAuth();
|
||||
} catch (\Exception $e) {
|
||||
http_response_code(401);
|
||||
exit($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$auth_user = UserModel::getAuthUser();
|
||||
if (!$auth_user) {
|
||||
$url = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
|
||||
@@ -302,6 +302,42 @@ class AdminController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles URL: /admin/auth/proxy/save
|
||||
*
|
||||
* @param Asatru\Controller\ControllerArg $request
|
||||
* @return Asatru\View\RedirectHandler
|
||||
*/
|
||||
public function save_proxy_auth_settings($request)
|
||||
{
|
||||
try {
|
||||
$auth_proxy_enable = (bool)$request->params()->query('auth_proxy_enable', 0);
|
||||
$auth_proxy_header_email = $request->params()->query('auth_proxy_header_email', '');
|
||||
$auth_proxy_header_username = $request->params()->query('auth_proxy_header_username', '');
|
||||
$auth_proxy_sign_up = (bool)$request->params()->query('auth_proxy_sign_up', 0);
|
||||
$auth_proxy_whitelist = $request->params()->query('auth_proxy_whitelist', '');
|
||||
$auth_proxy_hide_logout = (bool)$request->params()->query('auth_proxy_hide_logout', 0);
|
||||
|
||||
$set = [
|
||||
'auth_proxy_enable' => $auth_proxy_enable,
|
||||
'auth_proxy_header_email' => $auth_proxy_header_email,
|
||||
'auth_proxy_header_username' => $auth_proxy_header_username,
|
||||
'auth_proxy_sign_up' => $auth_proxy_sign_up,
|
||||
'auth_proxy_whitelist' => $auth_proxy_whitelist,
|
||||
'auth_proxy_hide_logout' => $auth_proxy_hide_logout
|
||||
];
|
||||
|
||||
AppModel::updateSet($set);
|
||||
|
||||
FlashMessage::setMsg('success', __('app.proxy_auth_settings_saved_successfully'));
|
||||
|
||||
return redirect('/admin?tab=auth');
|
||||
} catch (\Exception $e) {
|
||||
FlashMessage::setMsg('error', $e->getMessage());
|
||||
return redirect('/admin?tab=auth');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles URL: /admin/attribute/schema/add
|
||||
*
|
||||
|
||||
@@ -423,5 +423,14 @@ return [
|
||||
'confirm_remove_shared_photo' => 'Do you really want to remove this item?',
|
||||
'share_photo_public' => 'Share this photo publicly',
|
||||
'share_photo_description' => 'Description',
|
||||
'share_photo_keywords' => 'Keywords'
|
||||
'share_photo_keywords' => 'Keywords',
|
||||
'auth' => 'Auth',
|
||||
'auth_proxy_enable' => 'Enable Proxy Authentication',
|
||||
'auth_proxy_header_email' => 'HTTP Header for E-mail',
|
||||
'auth_proxy_header_username' => 'HTTP Header for Username',
|
||||
'auth_proxy_sign_up' => 'Automatically register unknown users',
|
||||
'auth_proxy_whitelist' => 'Proxy Address Whitelist',
|
||||
'auth_proxy_hide_logout' => 'Hide logout button',
|
||||
'proxy_auth_settings_saved_successfully' => 'Proxy authentication settings saved successfully',
|
||||
'auth_proxy_warning' => 'Warning: Be sure that your workspace instance is protected behind a proxy before enabling this feature'
|
||||
];
|
||||
@@ -61,6 +61,12 @@ class AppModel_Migration {
|
||||
$this->database->add('system_message_plant_log BOOLEAN NOT NULL DEFAULT 1');
|
||||
$this->database->add('auto_backup BOOLEAN NOT NULL DEFAULT 0');
|
||||
$this->database->add('backup_path VARCHAR(1024) NULL');
|
||||
$this->database->add('auth_proxy_enable BOOLEAN NOT NULL DEFAULT 0');
|
||||
$this->database->add('auth_proxy_header_email VARCHAR(512) NULL');
|
||||
$this->database->add('auth_proxy_header_username VARCHAR(512) NULL');
|
||||
$this->database->add('auth_proxy_sign_up BOOLEAN NOT NULL DEFAULT 0');
|
||||
$this->database->add('auth_proxy_whitelist TEXT NULL');
|
||||
$this->database->add('auth_proxy_hide_logout BOOLEAN NOT NULL DEFAULT 0');
|
||||
$this->database->add('created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP');
|
||||
$this->database->create();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,66 @@ class UserModel extends \Asatru\Database\Model {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function performProxyAuth()
|
||||
{
|
||||
try {
|
||||
if (!app('auth_proxy_enable')) {
|
||||
throw new \Exception('Feature is currently not active');
|
||||
}
|
||||
|
||||
$whitelist = app('auth_proxy_whitelist');
|
||||
if ((is_string($whitelist)) && (strlen($whitelist) > 0)) {
|
||||
$accepted = false;
|
||||
$remote_addr = $_SERVER['REMOTE_ADDR'];
|
||||
$whitelist = explode(PHP_EOL, $whitelist);
|
||||
|
||||
foreach ($whitelist as $address) {
|
||||
if ($address === $remote_addr) {
|
||||
$accepted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$accepted) {
|
||||
throw new \Exception('Unauthorized Proxy: ' . $remote_addr);
|
||||
}
|
||||
}
|
||||
|
||||
$header_email = $_SERVER['HTTP_' . strtoupper(str_replace('-', '_', app('auth_proxy_header_email')))] ?? null;
|
||||
if ((!is_string($header_email)) || (strlen($header_email) == 0)) {
|
||||
throw new \Exception('Invalid E-Mail header value provided: ' . $header_email);
|
||||
}
|
||||
|
||||
$header_username = $_SERVER['HTTP_' . strtoupper(str_replace('-', '_', app('auth_proxy_header_username')))] ?? null;
|
||||
if ((!is_string($header_username)) || (strlen($header_username) == 0)) {
|
||||
throw new \Exception('Invalid username header value provided: ' . $header_username);
|
||||
}
|
||||
|
||||
$authuser = static::raw('SELECT * FROM `' . self::tableName() . '` WHERE email = ?', [$header_email])->first();
|
||||
if (!$authuser) {
|
||||
if (app('auth_proxy_sign_up')) {
|
||||
static::createUser($header_username, $header_email, false);
|
||||
|
||||
$authuser = static::raw('SELECT * FROM `' . self::tableName() . '` WHERE email = ?', [$header_email])->first();
|
||||
} else {
|
||||
throw new \Exception('User not found: ' . $header_email);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$header_username !== $authuser->get('name')) {
|
||||
static::raw('UPDATE `' . self::tableName() . '` SET name = ? WHERE id = ?', [$header_username, $authuser->get('id')]);
|
||||
}
|
||||
|
||||
SessionModel::loginSession($authuser->get('id'), session_id());
|
||||
} catch (\Exception $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $email
|
||||
* @param $password
|
||||
|
||||
@@ -2443,6 +2443,36 @@ fieldset .field {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.admin-auth {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.admin-auth h2 {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.admin-auth label, .admin-auth span {
|
||||
color: rgb(150, 150, 150);
|
||||
}
|
||||
|
||||
.admin-auth input, .admin-auth textarea {
|
||||
color: rgb(150, 150, 150);
|
||||
background-color: rgb(50, 50, 50);
|
||||
}
|
||||
|
||||
.admin-auth-warning {
|
||||
position: relative;
|
||||
display: none;
|
||||
padding: 20px;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 25px;
|
||||
color: rgb(250, 250, 250);
|
||||
background-color: rgba(202, 102, 102, 0.76);
|
||||
border: 1px solid rgb(235, 150, 150);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.admin-attribute-schema-list {
|
||||
position: relative;
|
||||
}
|
||||
@@ -2861,7 +2891,7 @@ fieldset .field {
|
||||
margin-top: 30px;
|
||||
color: rgb(250, 250, 250);
|
||||
background-color: rgba(102, 202, 160, 0.76);
|
||||
border: 1px solid rgb(150, 236, 200);
|
||||
border: 1px solid rgb(150, 235, 200);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<li class="admin-tab-media {{ ((isset($_GET['tab'])) && ($_GET['tab'] === 'media')) ? 'is-active' : ''}}"><a href="javascript:void(0);" onclick="window.vue.switchAdminTab('media');">{{ __('app.admin_media') }}</a></li>
|
||||
<li class="admin-tab-users {{ ((isset($_GET['tab'])) && ($_GET['tab'] === 'users')) ? 'is-active' : ''}}"><a href="javascript:void(0);" onclick="window.vue.switchAdminTab('users');">{{ __('app.users') }}</a></li>
|
||||
<li class="admin-tab-locations {{ ((isset($_GET['tab'])) && ($_GET['tab'] === 'locations')) ? 'is-active' : ''}}"><a href="javascript:void(0);" onclick="window.vue.switchAdminTab('locations');">{{ __('app.locations') }}</a></li>
|
||||
<li class="admin-tab-auth {{ ((isset($_GET['tab'])) && ($_GET['tab'] === 'auth')) ? 'is-active' : ''}}"><a href="javascript:void(0);" onclick="window.vue.switchAdminTab('auth');">{{ __('app.auth') }}</a></li>
|
||||
<li class="admin-tab-attributes {{ ((isset($_GET['tab'])) && ($_GET['tab'] === 'attributes')) ? 'is-active' : ''}}"><a href="javascript:void(0);" onclick="window.vue.switchAdminTab('attributes');">{{ __('app.attributes') }}</a></li>
|
||||
<li class="admin-tab-calendar {{ ((isset($_GET['tab'])) && ($_GET['tab'] === 'calendar')) ? 'is-active' : ''}}"><a href="javascript:void(0);" onclick="window.vue.switchAdminTab('calendar');">{{ __('app.calendar') }}</a></li>
|
||||
<li class="admin-tab-mail {{ ((isset($_GET['tab'])) && ($_GET['tab'] === 'mail')) ? 'is-active' : ''}}"><a href="javascript:void(0);" onclick="window.vue.switchAdminTab('mail');">{{ __('app.mail') }}</a></li>
|
||||
@@ -324,6 +325,61 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-auth {{ ((!isset($_GET['tab'])) || ($_GET['tab'] !== 'auth')) ? 'is-hidden' : ''}}">
|
||||
<h2>{{ __('app.auth') }}</h2>
|
||||
|
||||
<form method="POST" action="{{ url('/admin/auth/proxy/save') }}">
|
||||
@csrf
|
||||
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input type="checkbox" name="auth_proxy_enable" value="1" onclick="if (this.checked) { document.querySelector('.admin-auth-warning').style.display = 'block'; } else { document.querySelector('.admin-auth-warning').style.display = 'none'; }" {{ ((app('auth_proxy_enable')) ? 'checked' : '') }}/> <span>{{ __('app.auth_proxy_enable') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-auth-warning">{{ __('app.auth_proxy_warning') }}</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">{{ __('app.auth_proxy_header_email') }}</label>
|
||||
<div class="control">
|
||||
<input type="text" class="input" name="auth_proxy_header_email" value="{{ app('auth_proxy_header_email') }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">{{ __('app.auth_proxy_header_username') }}</label>
|
||||
<div class="control">
|
||||
<input type="text" class="input" name="auth_proxy_header_username" value="{{ app('auth_proxy_header_username') }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input type="checkbox" name="auth_proxy_sign_up" value="1" {{ ((app('auth_proxy_sign_up')) ? 'checked' : '') }}/> <span>{{ __('app.auth_proxy_sign_up') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">{{ __('app.auth_proxy_whitelist') }}</label>
|
||||
<div class="control">
|
||||
<textarea class="textarea" name="auth_proxy_whitelist">{{ app('auth_proxy_whitelist') }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input type="checkbox" name="auth_proxy_hide_logout" value="1" {{ ((app('auth_proxy_hide_logout')) ? 'checked' : '') }}/> <span>{{ __('app.auth_proxy_hide_logout') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input type="submit" class="button is-success" value="{{ __('app.save') }}" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="admin-attributes {{ ((!isset($_GET['tab'])) || ($_GET['tab'] !== 'attributes')) ? 'is-hidden' : ''}}">
|
||||
<h2>{{ __('app.attributes') }}</h2>
|
||||
|
||||
|
||||
@@ -100,11 +100,13 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ((!app('auth_proxy_enable')) || (!app('auth_proxy_hide_logout')))
|
||||
<div class="navbar-item">
|
||||
<a href="{{ url('/logout') }}">
|
||||
<i class="fas fa-sign-out-alt" title="{{ __('app.logout') }}"></i><span class="navbar-item-only-mobile"> {{ __('app.logout') }}</span>
|
||||
</a>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="navbar-item has-dropdown is-hoverable">
|
||||
<a class="navbar-link navbar-dropdown-minwidth">
|
||||
|
||||
2
public/js/app.js
vendored
2
public/js/app.js
vendored
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user