Files
pathfinder/app/main/controller/admin.php
Exodus4D a8edf39697 - new "logging" system for map/system/signature/connection changes, closed #271
- new map change log to Slack channel
- new "rally point" logging to Slack channel
- new "rally point" poke options (e.g. custom message), closed #295
- new log options for WebSocket installations
- added ship "mass" logging (backend only), #313
- added map logging to Slack, #326
- added "ESI error rate" limit detection
- added "Monolog" as new logging library (Composer dependency)
- added "Swiftmailer" as new eMail library (Composer dependency)
- added Support for Redis session hander (performance boost)
- improved character select panels (visible "online" status)
- improved "activity logging" (more DB columns added to check)
- improved eMail logging (HTML template support)
- improved "delete map" now become "inactive" for some days before delete
- improved character logout handling
- improved /setup page for DB bootstrap (new button for DB create if not exists)
- fixed broken ship tracking (ship name re-added)
- fixed broken ship tracking for multiple chars on different browser tabs
- fixed broken cursor coordinates, closed #518
- fixed null pointer "charactermodel.php->isActive():925" closed #529
- fixed broken "scroll offset", closed #533 closed #534
- Updated "validation" library JS v0.10.1 -> v0.11.9
- Updated ORM Mapper _Cortex_ v1.5.0-dev -> v1.5.0
- and many more....
2017-10-22 17:58:34 +02:00

312 lines
10 KiB
PHP

<?php
/**
* Created by PhpStorm.
* User: exodu
* Date: 12.05.2017
* Time: 20:30
*/
namespace Controller;
use Controller\Ccp\Sso;
use Model\CharacterModel;
use Model\CorporationModel;
use lib\Config;
class Admin extends Controller{
const ERROR_SSO_CHARACTER_EXISTS = 'No character found. Please login first.';
const ERROR_SSO_CHARACTER_SCOPES = 'Additional ESI scopes are required for "%s". Use the SSO button below.';
const ERROR_SSO_CHARACTER_ROLES = 'Insufficient in-game roles. "%s" requires at least one of these corp roles: %s.';
const LOG_TEXT_KICK_BAN = '%s "%s" from corporation "%s", by "%s"';
const KICK_OPTIONS = [
5 => '5m',
60 => '1h',
1440 => '24h'
];
/**
* event handler for all "views"
* some global template variables are set in here
* @param \Base $f3
* @param $params
* @return bool
*/
function beforeroute(\Base $f3, $params): bool {
$return = parent::beforeroute($f3, $params);
$f3->set('tplPage', 'login');
if($character = $this->getAdminCharacter($f3)){
$f3->set('tplLogged', true);
$f3->set('character', $character);
$this->dispatch($f3, $params, $character);
}
$f3->set('tplAuthType', $f3->alias( 'sso', ['action' => 'requestAdminAuthorization']));
// page title
$f3->set('tplPageTitle', 'Admin | ' . Config::getPathfinderData('name'));
// main page content
$f3->set('tplPageContent', Config::getPathfinderData('view.admin'));
// body element class
$f3->set('tplBodyClass', 'pf-landing');
return $return;
}
/**
* event handler after routing
* @param \Base $f3
*/
public function afterroute(\Base $f3) {
// js view (file)
$f3->set('tplJsView', 'admin');
// render view
echo \Template::instance()->render( Config::getPathfinderData('view.index') );
// clear all SSO related temp data
if( $f3->exists(Sso::SESSION_KEY_SSO) ){
$f3->clear('SESSION.SSO.ERROR');
}
}
/**
* returns valid admin $characterModel for current user
* @param \Base $f3
* @return CharacterModel|null
*/
protected function getAdminCharacter(\Base $f3){
$adminCharacter = null;
if( !$f3->exists(Sso::SESSION_KEY_SSO_ERROR) ){
if( $character = $this->getCharacter() ){
$this->setCharacterRole($character);
if($character->role != 'MEMBER'){
// current character is admin
$adminCharacter = $character;
}elseif( !$character->hasAdminScopes() ){
$f3->set(Sso::SESSION_KEY_SSO_ERROR,
sprintf(
self::ERROR_SSO_CHARACTER_SCOPES,
$character->name
));
}else{
$f3->set(Sso::SESSION_KEY_SSO_ERROR,
sprintf(
self::ERROR_SSO_CHARACTER_ROLES,
$character->name,
implode(', ', CorporationModel::ADMIN_ROLES)
));
}
}else{
$f3->set(Sso::SESSION_KEY_SSO_ERROR, self::ERROR_SSO_CHARACTER_EXISTS);
}
}
return $adminCharacter;
}
/**
* set temp "virtual" field with current admin role name for a $characterModel
* @param CharacterModel $character
*/
protected function setCharacterRole(CharacterModel $character){
$character->virtual('role', function ($character){
// default role based on roleId (auto-detected)
if(($role = array_search($character->roleId, CharacterModel::ROLES)) === false){
$role = 'MEMBER';
}
/**
* check config files for hardcoded character roles
* -> overwrites default role (e.g. auto detected by corp in-game roles)
*/
if($this->getF3()->exists('PATHFINDER.ADMIN.CHARACTER', $globalAdminData)){
foreach((array)$globalAdminData as $adminData){
if($adminData[ 'ID' ] === $character->_id){
if(CharacterModel::ROLES[ $adminData[ 'ROLE' ] ]){
$role = $adminData[ 'ROLE' ];
}
break;
}
}
}
return $role;
});
}
/**
* dispatch page events by URL $params
* @param \Base $f3
* @param array $params
* @param null $character
*/
public function dispatch(\Base $f3, $params, $character = null){
if($character instanceof CharacterModel){
// user logged in
$parts = array_values(array_filter(array_map('strtolower', explode('/', $params['*']))));
$f3->set('tplPage', $parts[0]);
switch($parts[0]){
case 'settings':
break;
case 'members':
switch($parts[1]){
case 'kick':
$objectId = (int)$parts[2];
$value = (int)$parts[3];
$this->kickCharacter($character, $objectId, $value);
$f3->reroute('@admin(@*=/' . $parts[0] . ')');
break;
case 'ban':
$objectId = (int)$parts[2];
$value = (int)$parts[3];
$this->banCharacter($character, $objectId, $value);
break;
}
$f3->set('tplPage', 'members');
$f3->set('tplKickOptions', self::KICK_OPTIONS);
$this->initMembers($f3, $character);
break;
case 'login':
default:
$f3->set('tplPage', 'login');
break;
}
}
}
/**
* kick or revoke a character
* @param CharacterModel $character
* @param int $kickCharacterId
* @param int $minutes
*/
protected function kickCharacter(CharacterModel $character, $kickCharacterId, $minutes){
$kickOptions = self::KICK_OPTIONS;
$minKickTime = key($kickOptions) ;
end($kickOptions);
$maxKickTime = key($kickOptions);
$minutes = in_array($minutes, range($minKickTime, $maxKickTime)) ? $minutes : 0;
$kickCharacters = $this->filterValidCharacters($character, $kickCharacterId);
foreach($kickCharacters as $kickCharacter){
$kickCharacter->kick($minutes);
$kickCharacter->save();
self::getLogger()->write(
sprintf(
self::LOG_TEXT_KICK_BAN,
$minutes ? 'KICK' : 'KICK REVOKE',
$kickCharacter->name,
$kickCharacter->getCorporation()->name,
$character->name
)
);
}
}
/**
* @param CharacterModel $character
* @param int $banCharacterId
* @param int $value
*/
protected function banCharacter(CharacterModel $character, $banCharacterId, $value){
$banCharacters = $this->filterValidCharacters($character, $banCharacterId);
foreach($banCharacters as $banCharacter){
$banCharacter->ban($value);
$banCharacter->save();
self::getLogger()->write(
sprintf(
self::LOG_TEXT_KICK_BAN,
$value ? 'BAN' : 'BAN REVOKE',
$banCharacter->name,
$banCharacter->getCorporation()->name,
$character->name
)
);
}
}
/**
* checks whether a $character has admin access rights for $charcterId
* -> must be in same corporation
* @param CharacterModel $character
* @param int $characterId
* @return array|\DB\CortexCollection
*/
protected function filterValidCharacters(CharacterModel $character, $characterId){
$characters = [];
// check if kickCharacters belong to same Corp as admin character
// -> remove admin char from valid characters...
if( !empty($characterIds = array_diff( [$characterId], [$character->_id])) ){
if($character->role === 'SUPERADMIN'){
if($filterCharacters = CharacterModel::getAll($characterIds)){
$characters = $filterCharacters;
}
}else{
$characters = $character->getCorporation()->getCharacters($characterIds);
}
}
return $characters;
}
/**
* get log file for "admin" logs
* @param string $type
* @return \Log
*/
static function getLogger($type = 'ADMIN'){
return parent::getLogger('ADMIN');
}
/**
* init /member page data
* @param \Base $f3
* @param CharacterModel $character
*/
protected function initMembers(\Base $f3, CharacterModel $character){
$data = (object) [];
if($characterCorporation = $character->getCorporation()){
$corporations = [];
switch($character->role){
case 'SUPERADMIN':
if($accessCorporations = CorporationModel::getAll(['addNPC' => true])){
$corporations = $accessCorporations;
}
break;
case 'CORPORATION':
$corporations[] = $characterCorporation;
break;
}
foreach($corporations as $corporation){
if($characters = $corporation->getCharacters()){
$data->corpMembers[$corporation->name] = $corporation->getCharacters();
}
}
// sort corporation from current user first
if( !empty($data->corpMembers[$characterCorporation->name]) ){
$data->corpMembers = array($characterCorporation->name => $data->corpMembers[$characterCorporation->name]) + $data->corpMembers;
}
}
$f3->set('tplMembers', $data);
}
}