src/Security/Voter/ApiVoter.php line 11

Open in your IDE?
  1. <?php
  2. namespace App\Security\Voter;
  3. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  4. use Symfony\Component\Security\Core\Authorization\Voter\Voter;
  5. use Symfony\Component\Security\Core\User\UserInterface;
  6. use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
  7. use Symfony\Component\Security\Core\Security;
  8. class ApiVoter extends Voter
  9. {
  10.     private $security;
  11.     
  12.     public function __construct(Security $security)
  13.     {
  14.         $this->security $security;
  15.     }
  16.     
  17.     protected function supports($attribute$subject) : bool
  18.     {
  19.         if(in_array($attribute, ['API_PASS_THROUGH'])) {
  20.             return true;
  21.         }            
  22.         
  23.         return false;
  24.     }
  25.     
  26.     protected function voteOnAttribute($attribute$subjectTokenInterface $token) : bool
  27.     {
  28.         $user $token->getUser();
  29.         // if the user is anonymous, do not grant access
  30.         if (!$user instanceof UserInterface) {
  31.             return false;
  32.         }
  33.         
  34.         switch ($attribute) {
  35.             case 'API_PASS_THROUGH':
  36.                 if($this->_isCommandAllowed($subject)) {
  37.                     return $this->_isUserAllowed($subject$user);
  38.                 }
  39.                 else {
  40.                     return false;
  41.                 }
  42.                 
  43.                 break;                    
  44.         }
  45.         
  46.         return false;
  47.     }
  48.     
  49.     private function _commandsAllowed()
  50.     {
  51.         $cmds = [];
  52.         $cmds[] = 'accountdt/'// POST
  53.         $cmds[] = 'aliasdt/'// POST
  54.         $cmds[] = 'account/check/'// GET user/domain
  55.         $cmds[] = 'account/checkwithalias/'// GET user/domain
  56.         $cmds[] = 'account/checkpasswordreq/'// GET password
  57.         $cmds[] = 'account/get/'// GET user/domain
  58.         $cmds[] = 'account/getinprogress/'// GET user/domain
  59.         $cmds[] = 'account/getquota/'// GET user/domain
  60.         $cmds[] = 'account/getroledomains/'// GET user/domain
  61.         $cmds[] = 'account/list/'// GET filter/filter_params
  62.         $cmds[] = 'account/isinprogress/'// GET src/dest
  63.         $cmds[] = 'account/saveextparameters'// POST
  64.         $cmds[] = 'account/getimapdircount/'// GET user/domain/directory
  65.         $cmds[] = 'alias/get'// GET src/dest
  66.         $cmds[] = 'alias/getinprogress/'// GET src/dest
  67.         $cmds[] = 'alias/isinprogress/'// GET src/dest
  68.         $cmds[] = 'domain/list'// GET no params
  69.         $cmds[] = 'domain/getaliases'// GET domain
  70.         $cmds[] = 'logs/dtprocess';
  71.         $cmds[] = 'logs/dtprocessmaster';
  72.         $cmds[] = 'charts/';
  73.         $cmds[] = 'login_msg/userdismissed';
  74.         
  75.         return $cmds;
  76.     }
  77.     
  78.     private function _isCommandAllowed($cmdData)
  79.     {
  80.         $cmd $cmdData['command'];
  81.         foreach($this->_commandsAllowed() as $c) {
  82.             if(preg_match('/^' str_replace('/''\/'$c) . '/'$cmd)) {
  83.                 return true;
  84.             }
  85.         }
  86.         
  87.         return false;
  88.     }
  89.     
  90.     private function _isUserAllowed($cmdData$user)
  91.     {
  92.         $cmd $cmdData['command'];
  93.         
  94.         if(array_key_exists('data'$cmdData)) {
  95.             $data $cmdData['data'];
  96.         }
  97.         else {
  98.             $data null;
  99.         }
  100.         
  101.         $cmdParams $this->_splitCmdParams($cmd);
  102.                     
  103.         if($this->security->isGranted('ROLE_ADMIN')) {
  104.             if($this->_checkAdminPerms($cmdParams[0], $cmdParams[1], $data$user)) {
  105.                 return true;
  106.             }
  107.             // else - check permissions for postmaster and user
  108.         }
  109.         
  110.         if($this->security->isGranted('ROLE_POSTMASTER')) {
  111.             return $this->_checkPostmasterPerms($cmdParams[0], $cmdParams[1], $data$user);
  112.         }
  113.         else {
  114.             if($this->security->isGranted('ROLE_POSTMASTER_MIN')) {
  115.                 return $this->_checkPostmasterMinPerms($cmdParams[0], $cmdParams[1], $data$user);
  116.             }
  117.             else {
  118.                 if($this->security->isGranted('ROLE_GROUPMASTER')) {
  119.                     return $this->_checkGroupmasterPerms($cmdParams[0], $cmdParams[1], $data$user);
  120.                 }
  121.                 else {
  122.                     if($this->security->isGranted('ROLE_USERMASTER')) {
  123.                         return $this->_checkUsermasterPerms($cmdParams[0], $cmdParams[1], $data$user);
  124.                     }
  125.                     else {
  126.                         if($this->security->isGranted('ROLE_USER')) {
  127.                             return $this->_checkUserPerms($cmdParams[0], $cmdParams[1], $data$user->getEmail());
  128.                         }
  129.                     }
  130.                 }
  131.             }
  132.         }
  133.         return false;
  134.     }
  135.     
  136.     // returns command itself in ret[0] and params in ret[1]
  137.     private function _splitCmdParams($cmd)
  138.     {
  139.         $exploded explode('/'$cmd);
  140.         $ret = [];
  141.         $ret[0] = '';
  142.         $ret[1] = '';
  143.         
  144.         for($i 0$i sizeof($exploded); $i++) {
  145.             if($i 2) {
  146.                 $ret[0] = $ret[0] . '/' $exploded[$i];
  147.             }
  148.             else {
  149.                 $ret[1] = $ret[1] . '/' $exploded[$i];
  150.             } 
  151.         }
  152.         
  153.         $ret[0] = ltrim($ret[0], '/');
  154.         $ret[1] = ltrim($ret[1], '/');
  155.         return $ret;
  156.     }
  157.     
  158.     // checks if user is permitted to execute the command
  159.     private function _checkUserPerms($cmd$params$postData$userEmail)
  160.     {
  161.         $userSlash str_replace('@''/'$userEmail);
  162.         if($cmd == 'account/checkpasswordreq') {
  163.             return true;
  164.         }
  165.         elseif($cmd == 'account/get') {
  166.             // get allowed only for its own account
  167.             if($userSlash == $params) {
  168.                 return true;
  169.             }
  170.             else {
  171.                 return false;
  172.             }
  173.         }
  174.         elseif($cmd == 'account/saveextparameters') {
  175.             if($postData['userName'] . '/' $postData['userDomain'] == $userSlash) {
  176.                 return true;
  177.             }
  178.             return false;
  179.         }
  180.         elseif($cmd == 'account/getimapdircount') {
  181.             // demove dirname from parameters
  182.             $paramsArr explode('/'$params);
  183.             
  184.             if(count($paramsArr) != 3) {
  185.                 return false;
  186.             }
  187.             
  188.             $userDomainFromCmd $paramsArr[0] . '/' $paramsArr[1];
  189.             
  190.             if($userSlash == $userDomainFromCmd) {
  191.                 return true;
  192.             }
  193.             else {
  194.                 return false;
  195.             }
  196.         }
  197.         elseif($cmd == 'login_msg/userdismissed') {
  198.             // demove msg_id from parameters
  199.             $paramsArr explode('/'$params);
  200.             
  201.             if(count($paramsArr) != 3) {
  202.                 return false;
  203.             }
  204.             
  205.             $userDomainFromCmd $paramsArr[0] . '/' $paramsArr[1];
  206.             
  207.             if($userSlash == $userDomainFromCmd) {
  208.                 return true;
  209.             }
  210.             else {
  211.                 return false;
  212.             }
  213.         }
  214.         
  215.         
  216.         return false;
  217.     }
  218.     
  219.     // checks if postmaster is permitted to execute the command
  220.     private function _checkPostmasterPerms($cmd$params$postData$user)
  221.     {
  222.         // check domain
  223.         switch($cmd) {
  224.             case 'account/check':
  225.             case 'account/checkwithalias':
  226.             case 'account/get':
  227.             case 'account/getinprogress':
  228.             case 'account/getquota':
  229.             case 'account/getroledomains':
  230.             case 'account/isinprogress':
  231.                 return $this->_checkDomain($params$user->getDomains());
  232.                 
  233.             case 'account/getimapdircount':
  234.                 // in $params is also directory, give only user/domain
  235.                 return $this->_checkDomain(implode('/'array_slice(explode('/'$params), 02)), $user->getDomains());
  236.             case 'account/checkpasswordreq':
  237.             case 'domain/list':
  238.                 return true;
  239.                 
  240.             case 'account/saveextparameters':
  241.                 return $this->_checkDomain($postData['userName'] . '/' $postData['userDomain'], $user->getDomains());
  242.                 
  243.             case 'alias/get':
  244.             case 'alias/getinprogress':
  245.             case 'alias/isinprogress':
  246.                 $srcDest explode('/'$params);
  247.                 return $this->_checkDomain(str_replace('@''/'$srcDest[0]), $user->getDomains());
  248.                 
  249.             case 'logs/dtprocess':
  250.                 // only for current logged in user
  251.                 // in $params can be string filter/JSON_OBJECT
  252.                 
  253.                 $filterArr explode('/'$params);
  254.                                 
  255.                 if(count($filterArr) < 2) {
  256.                     return false;
  257.                 }
  258.                 
  259.                 $filter json_decode(urldecode($filterArr[1]));
  260.                                 
  261.                 if(!is_null($filter) && isset($filter->user)) {
  262.                     if($filter->user == $user->getEmail()) {
  263.                         return true;
  264.                     }
  265.                     else {
  266.                         return false;
  267.                     }
  268.                 }
  269.                 
  270.                 return false;
  271.                 
  272.             case 'logs/dtprocessmaster':
  273.                 return false;
  274.          
  275.             case 'account/list':
  276.             case 'accountdt/list':
  277.             case 'aliasdt/list':
  278.                 // domains may be in POST
  279.                 if(array_key_exists('filter'$postData)) {
  280.                     $filter json_decode($postData['filter'], true);
  281.                     
  282.                     if(array_key_exists('domains'$filter)) {
  283.                         $domains $filter['domains'];
  284.                     }
  285.                     else {
  286.                         $domains $this->_getDomainsFromFilter($params);
  287.                     }
  288.                 }
  289.                 else {
  290.                     $domains $this->_getDomainsFromFilter($params);
  291.                 }
  292.                 
  293.                 if(count($domains) == 0) {
  294.                     return false;
  295.                 }
  296.                 
  297.                 $userDomains $user->getDomains();
  298.                 
  299.                 foreach($domains as $d) {
  300.                     if($this->_checkDomain('x/' $d$userDomains) == false) {
  301.                         return false;
  302.                     }
  303.                 }
  304.             
  305.                 return true;
  306.                 
  307.             default:
  308.                 return false;
  309.         }
  310.         
  311.         return false;
  312.     }
  313.     // checks if postmaster min is permitted to execute the command
  314.     private function _checkPostmasterMinPerms($cmd$params$postData$user)
  315.     {
  316.         // check domain
  317.         switch($cmd) {
  318.             case 'account/check':
  319.             case 'account/checkwithalias':
  320.             case 'account/get':
  321.             case 'account/getinprogress':
  322.             case 'account/getquota':
  323.             case 'account/getroledomains':
  324.             case 'account/isinprogress':
  325.                 return $this->_checkDomain($params$user->getDomains());
  326.                 
  327.             case 'account/getimapdircount':
  328.                 // in $params is also directory, give only user/domain
  329.                 return $this->_checkDomain(implode('/'array_slice(explode('/'$params), 02)), $user->getDomains());
  330.             case 'account/checkpasswordreq':
  331.             case 'domain/list':
  332.                 return true;
  333.                                 
  334.             case 'alias/get':
  335.             case 'alias/getinprogress':
  336.             case 'alias/isinprogress':
  337.                 $srcDest explode('/'$params);
  338.                 return $this->_checkDomain(str_replace('@''/'$srcDest[0]), $user->getDomains());
  339.                 
  340.             case 'logs/dtprocess':
  341.                 // only for current logged in user
  342.                 // in $params can be string filter/JSON_OBJECT
  343.                 
  344.                 $filterArr explode('/'$params);
  345.                                 
  346.                 if(count($filterArr) < 2) {
  347.                     return false;
  348.                 }
  349.                 
  350.                 $filter json_decode(urldecode($filterArr[1]));
  351.                                 
  352.                 if(!is_null($filter) && isset($filter->user)) {
  353.                     if($filter->user == $user->getEmail()) {
  354.                         return true;
  355.                     }
  356.                     else {
  357.                         return false;
  358.                     }
  359.                 }
  360.                 
  361.                 return false;
  362.                 
  363.             case 'logs/dtprocessmaster':
  364.                 return false;
  365.          
  366.             case 'account/list':
  367.             case 'accountdt/list':
  368.             case 'aliasdt/list':
  369.                 // domains may be in POST
  370.                 if(array_key_exists('filter'$postData)) {
  371.                     $filter json_decode($postData['filter'], true);
  372.                     
  373.                     if(array_key_exists('domains'$filter)) {
  374.                         $domains $filter['domains'];
  375.                     }
  376.                     else {
  377.                         $domains $this->_getDomainsFromFilter($params);
  378.                     }
  379.                 }
  380.                 else {
  381.                     $domains $this->_getDomainsFromFilter($params);
  382.                 }
  383.                 
  384.                 if(count($domains) == 0) {
  385.                     return false;
  386.                 }
  387.                 
  388.                 $userDomains $user->getDomains();
  389.                 
  390.                 foreach($domains as $d) {
  391.                     if($this->_checkDomain('x/' $d$userDomains) == false) {
  392.                         return false;
  393.                     }
  394.                 }
  395.             
  396.                 return true;
  397.                 
  398.             default:
  399.                 return false;
  400.         }
  401.         
  402.         return false;
  403.     }
  404.     // checks if groupmaster is permitted to execute the command
  405.     private function _checkGroupmasterPerms($cmd$params$postData$user)
  406.     {
  407.         // check domain
  408.         switch($cmd) {
  409.             case 'account/check':
  410.             case 'account/checkwithalias':
  411.             case 'account/get':
  412.             case 'account/getinprogress':
  413.             case 'account/getquota':
  414.             case 'account/getroledomains':
  415.             case 'account/isinprogress':
  416.                 return $this->_checkDomain($params$user->getDomains()) && $this->_checkAccount($params$user->getAccounts());
  417.                 
  418.             case 'account/getimapdircount':
  419.                 // in $params is also directory, give only user/domain
  420.                 $cmdUserDomain implode('/'array_slice(explode('/'$params), 02));
  421.                 return $this->_checkDomain($cmdUserDomain$user->getDomains()) && $this->_checkAccount($cmdUserDomain$user->getAccounts());
  422.                 
  423.             case 'account/checkpasswordreq':
  424.             case 'domain/list':
  425.                 return true;
  426.                 
  427.             case 'account/saveextparameters':
  428.                 return $this->_checkDomain($postData['userName'] . '/' $postData['userDomain'], $user->getDomains())
  429.                 && $this->_checkAccount($postData['userName'] . '/' $postData['userDomain'], $user->getAccounts())
  430.                 ;
  431.                 
  432.             case 'alias/get':
  433.             case 'alias/getinprogress':
  434.             case 'alias/isinprogress':
  435.                 $srcDest explode('/'$params);
  436.                 return $this->_checkDomain(str_replace('@''/'$srcDest[1]), $user->getDomains())
  437.                 && $this->_checkAccount(str_replace('@''/'$srcDest[1]), $user->getAccounts())
  438.                 ;
  439.                 
  440.             case 'logs/dtprocess':
  441.             case 'logs/dtprocessmaster':
  442.                 return false;
  443.                 
  444.             case 'account/list':
  445.             case 'accountdt/list':
  446.             case 'aliasdt/list':
  447.                 // don't allow without parameter "groupmaster"
  448.                 if($this->_getGroupmasterFromFilter($params) == "") {
  449.                     return false;
  450.                 }
  451.                 
  452.                 $domains $this->_getDomainsFromFilter($params);
  453.                 
  454.                 if(count($domains) == 0) {
  455.                     return false;
  456.                 }
  457.                 
  458.                 $userDomains $user->getDomains();
  459.                 
  460.                 foreach($domains as $d) {
  461.                     if($this->_checkDomain('x/' $d$userDomains) == false) {
  462.                         return false;
  463.                     }
  464.                 }
  465.                 return true;
  466.                 
  467.             default:
  468.                 return false;
  469.         }
  470.         
  471.         return false;
  472.     }
  473.     
  474.     // checks if usermaster is permitted to execute the command
  475.     private function _checkUsermasterPerms($cmd$params$postData$user)
  476.     {
  477.         // check domain
  478.         switch($cmd) {
  479.             case 'account/check':
  480.             case 'account/checkwithalias':
  481.             case 'account/get':
  482.             case 'account/getinprogress':
  483.             case 'account/getquota':
  484.             case 'account/getroledomains':
  485.             case 'account/isinprogress':
  486.                 return $this->_checkDomain($params$user->getDomains()) && $this->_checkAccount($params$user->getAccounts());
  487.                 
  488.             /*case 'domain/list':
  489.                 return true;*/
  490.                 
  491.                 
  492.             case 'account/list':
  493.             case 'accountdt/list':
  494.                 // don't allow without parameter "groupmaster"
  495.                 if($this->_getGroupmasterFromFilter($params) == "") {
  496.                     return false;
  497.                 }
  498.                 
  499.                 $domains $this->_getDomainsFromFilter($params);
  500.                 
  501.                 if(count($domains) == 0) {
  502.                     return false;
  503.                 }
  504.                 
  505.                 $userDomains $user->getDomains();
  506.                 
  507.                 foreach($domains as $d) {
  508.                     if($this->_checkDomain('x/' $d$userDomains) == false) {
  509.                         return false;
  510.                     }
  511.                 }
  512.                 
  513.                 return true;
  514.                 
  515.             default:
  516.                 return false;
  517.         }
  518.         
  519.         return false;
  520.     }
  521.     // checks if admin is permitted to execute the command
  522.     // (commands specific to admins, if returns false check also postmaster permissions)
  523.     private function _checkAdminPerms($cmd$params$postData$user)
  524.     {
  525.         // check command
  526.         switch($cmd) {
  527.             case 'accountdt/rolestableall':
  528.             case 'accountdt/rolestableselected':
  529.             case 'accountdt/rolestoselected':
  530.             case 'accountdt/rolesfromselected':
  531.             case 'accountdt/list':
  532.             case 'account/list':
  533.             case 'aliasdt/list':
  534.             case 'domain/getaliases':
  535.             case 'logs/dtprocess':
  536.             case 'logs/dtprocessmaster':
  537.             case 'charts/loadavg':
  538.             case 'charts/freemem':
  539.             case 'charts/usedmem':
  540.             case 'charts/diskstats':
  541.             case 'charts/mailqueue':
  542.             case 'charts/connections':
  543.                 return true;
  544.             default:
  545.                 return false;
  546.         }
  547.         
  548.         return false;
  549.     }
  550.     
  551.     // checks if domain from $userAndDomain matches any domain from $domains 
  552.     private function _checkDomain($userAndDomain$domains)
  553.     {
  554.         $arr explode('/'$userAndDomain);
  555.         
  556.         if(sizeof($arr) != 2) {
  557.             return false;
  558.         }
  559.         
  560.         foreach($domains as $d) {
  561.             if($arr[1] == $d) {
  562.                 return true;
  563.             }
  564.         }
  565.         
  566.         return false;
  567.     }
  568.     
  569.     // checks if $userAndDomain matches user from $accounts
  570.     private function _checkAccount($userAndDomain$accounts)
  571.     {
  572.         $arr explode('/'$userAndDomain);
  573.         
  574.         if(sizeof($arr) != 2) {
  575.             return false;
  576.         }
  577.         
  578.         $user implode('@'$arr);
  579.         
  580.         foreach($accounts as $a) {
  581.             if($user == $a) {
  582.                 return true;
  583.             }
  584.         }
  585.         
  586.         return false;
  587.     }
  588.     // returns array of domains got from $params
  589.     private function _getDomainsFromFilter($params) : array
  590.     {
  591.         if(substr_count($params'filter/') == 0) {
  592.             return [];
  593.         }
  594.         
  595.         $paramsArr explode('/'$params);
  596.         
  597.         for($i 0$i count($paramsArr); $i++) {
  598.             if($paramsArr[$i] == 'filter') {
  599.                 $js json_decode(urldecode($paramsArr[$i+1]));
  600.                 
  601.                 if($js == null) {
  602.                     return [];
  603.                 }
  604.                 
  605.                 if(!property_exists($js"domains")) {
  606.                     return [];
  607.                 }
  608.                 
  609.                 return $js->domains;
  610.             }
  611.         }
  612.         
  613.         return [];
  614.     }
  615.     
  616.     // returns groupmaster from $params (or empty string if none)
  617.     private function _getGroupmasterFromFilter($params) : string
  618.     {
  619.         if(substr_count($params'filter/') == 0) {
  620.             return "";
  621.         }
  622.         
  623.         $paramsArr explode('/'urldecode($params));
  624.         
  625.         for($i 0$i count($paramsArr); $i++) {
  626.             if($paramsArr[$i] == 'filter') {
  627.                 
  628.                 $js json_decode(urldecode($paramsArr[$i+1]));
  629.                 
  630.                 if($js == null) {
  631.                     return "";
  632.                 }
  633.                 
  634.                 if(!property_exists($js"groupmaster")) {
  635.                     return "";
  636.                 }
  637.                 return $js->groupmaster;
  638.             }
  639.         }
  640.         
  641.         return "";
  642.     }
  643.     
  644. }