1<?php 2// must be run within Dokuwiki 3if (!defined('DOKU_INC')) die(); 4/** 5 * Two Factor Action Plugin 6 * 7 * @author Mike Wilmes mwilmes@avc.edu 8 * Big thanks to Daniel Popp and his Google 2FA code (authgoogle2fa) as a 9 * starting reference. 10 * 11 * Overview: 12 * The plugin provides for two opportunities to perform two factor 13 * authentication. The first is on the main login page, via a code provided by 14 * an external authenticator. The second is at a separate prompt after the 15 * initial login. By default, all modules will process from the second login, 16 * but a module can subscribe to accepting a password from the main login when 17 * it makes sense, because the user has access to the code in advance. 18 * 19 * If a user only has configured modules that provide for login at the main 20 * screen, the code will only be accepted at the main login screen for 21 * security purposes. 22 * 23 * Modules will be called to render their configuration forms on the profile 24 * page and to verify a user's submitted code. If any module accepts the 25 * submitted code, then the user is granted access. 26 * 27 * Each module may be used to transmit a message to the user that their 28 * account has been logged into. One module may be used as the default 29 * transmit option. These options are handled by the parent module. 30 */ 31class action_plugin_autogroup extends DokuWiki_Action_Plugin { 32 33 protected $_disabled = ''; // if disabled set to explanatory string 34 protected $_group_cfg = null; // The group management rules 35 protected $_add = null; // A flag to add when any rule matches 36 protected $_remove = null; // A flag to remove when all rules do not match 37 protected $_in_proc = false; // A locking flag to keep a group change from triggering this module recursively. 38 39 public function __construct(){ 40 $this->setupLocale(); 41 $this->_add = (bool)$this->getConf('enable_add'); 42 $this->_remove = (bool)$this->getConf('enable_remove'); 43 $raw_cfg = array_map(function($x){return explode(',',$x,3);}, explode("\n", $this->getConf('regex'))); 44 $this->_group_cfg = array(); 45 foreach($raw_cfg as $set) { 46 $key = array_shift($set); 47 $this->_group_cfg[$key][]=$set; 48 } 49 $this->_disabled = ( !$this->_add && !$this->_remove ) || count($this->_group_cfg)==0; 50 } 51 52 /** 53 * Registers the event handlers. 54 */ 55 public function register(Doku_Event_Handler $controller) 56 { 57 // In case this was called during setup, call the init again to have a chance to get the authentication module. 58 if (!$this->_disabled) { 59 // Update a single user after their info changes. 60 $controller->register_hook('AUTH_USER_CHANGE', 'AFTER', $this, 'update_group_event'); 61 62 // USE THIS IF WE GET THAT CONFIG CHANGE EVENT!!! 63 // Snag this event to update all users if a change was made on a config page. 64 //$controller->register_hook('CONFIG_CHANGE_MADE', 'AFTER', $this, 'check_update_all_event'); 65 66 // Snag this event to update all users if a change was made on a config page. 67 $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'check_update_all_event'); 68 } 69 } 70 71 /** 72 * Does the group list processing. 73 */ 74 public function update_group_event(&$event, $param) { 75 global $INPUT; 76 // If this module processing triggered this event, then abort. 77 if ($this->_in_proc) { return; } 78 // If disabled, then silently return. 79 if ($this->_disabled) { 80 return; 81 } 82 // If we can't read the user accounts or change the groups using the auth module, then abort. 83 global $auth; 84 if (!$auth || !$auth->canDo('getUsers') || !$auth->canDo('modGroups')) { 85 $msg = $this->getLang('nosupport'); 86 msg($msg, -1); 87 dbglog($msg); 88 return; 89 } 90 91 // If we have a logged in user, then process. 92 $user = $INPUT->server->str('REMOTE_USER', null); 93 if ($user) { $this->_update_user_groups($user); } 94 return; 95 } 96 97 /** 98 * Does the group list processing. 99 */ 100 private function _update_user_groups($user) { 101 global $auth; 102 103 // Set the event lock. 104 $this->_in_proc = true; 105 106 // Get this user's current group data. 107 $oldinfo = $auth->getUserData($user); 108 $oldinfo['user']=$user; 109 $oldgrps = $oldinfo['grps']; 110 $newgrps = array_values($oldgrps); 111 112 // Start validating regex rules. 113 $in = array(); 114 $out = array(); 115 foreach ($this->_group_cfg as $group=>$lines){ 116 $match = false; 117 foreach ($lines as $line) { 118 list($attr, $regex) = $line; 119 $match |= preg_match($regex, $oldinfo[$attr]); 120 } 121 if ($match) { 122 if ($this->_add){ 123 $in[] = $group; 124 if (!in_array($group,$newgrps)){ 125 $newgrps[] = $group; 126 } 127 } 128 } 129 else { 130 if ($this->_remove){ 131 $out[] = $group; 132 if (in_array($group,$newgrps)){ 133 array_splice($newgrps, array_keys($newgrps, $group)[0] , 1); 134 } 135 } 136 } 137 } 138 139 if ( $newgrps != $oldgrps ) { 140 $changes['grps'] = $newgrps; 141 } 142 143 if ($auth->triggerUserMod('modify', array($user, $changes))) { 144 $msg = sprintf($this->getLang('update_ok'), $user, implode(',',$in), implode(',',$out)); 145 dbglog($msg); 146 } 147 148 // Clear the event lock. 149 $this->_in_proc = false; 150 } 151 152 /** 153 * Used to update all groups for all users on the wiki. 154 * Might take a while on large wikis. 155 */ 156 public function update_all_groups() { 157 // If disabled, then silently return. 158 if ($this->_disabled) { 159 return; 160 } 161 // If we can't read the user accounts or change the groups using the auth module, then abort. 162 global $auth; 163 if (!$auth || !$auth->canDo('getUsers') || !$auth->canDo('modGroups')) { 164 $msg = $this->getLang('nosupport'); 165 msg($msg, -1); 166 dbglog($msg); 167 return; 168 } 169 // Get the user count and the list of users. 170 $userCount = $auth->getUserCount(); 171 $userData = $auth->retrieveUsers(0, $userCount, array()); 172 // Update groups for each user. 173 foreach ($userData as $user => $userinfo) { 174 $this->_update_user_groups($user); 175 } 176 } 177 178 public function check_update_all_event(&$event, $param){ 179 if ($_SESSION['PLUGIN_CONFIG']['state'] == 'updated') { 180 $this->update_all_groups(); 181 } 182 } 183}