1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
4 require_once 'MTrack/auth/http.php';
5 //require_once 'auth/openid.php';
7 require_once 'MTrack/DB.php';
8 require_once 'MTrack/Config.php';
10 require_once 'MTrack/Interface/Auth.php';
14 static $stack = array();
15 static $mechs = array();
16 static $group_assoc = array();
18 public static function registerMech(IMTrackAuth $mech) {
19 self::$mechs[] = $mech;
23 public static function su($user) {
24 if (!strlen($user)) throw new Exception("invalid user");
25 array_unshift(self::$stack, $user);
28 /** returns the instance of an auth mechanism given its class name */
29 public static function getMech($name) {
30 foreach (self::$mechs as $inst) {
31 if ($inst instanceof $name) {
38 /** drop identity set by last su */
39 public static function drop() {
40 if (count(self::$stack) == 0) {
41 throw new Exception("no privs to drop");
43 return array_shift(self::$stack);
46 /** returns the authenticated user, or null if authentication
48 public static function authenticate() {
49 foreach (self::$mechs as $mech) {
50 $name = $mech->authenticate();
56 /* always fall back on the unix username when running from
58 if (php_sapi_name() == 'cli') {
59 static $envs = array('MTRACK_LOGNAME', 'LOGNAME', 'USER');
60 foreach ($envs as $name) {
61 if (isset($_ENV[$name])) {
65 } elseif (count(self::$mechs) == 0 &&
66 MTrackConfig::get('core', 'admin_party') == 1
67 && ($_SERVER['REMOTE_ADDR'] == '127.0.0.1' ||
68 $_SERVER['REMOTE_ADDR'] == '::1')) {
75 public static function isAuthConfigured() {
76 return count(self::$mechs) ? true : false;
79 /** determine the current identity. If doauth is true (default),
80 * then the authentication hook will be invoked */
81 public static function whoami($doauth = true) {
82 if (count(self::$stack) == 0 && $doauth) {
84 $who = self::authenticate();
86 foreach (self::$mechs as $mech) {
87 $who = $mech->doAuthenticate();
96 } catch (Exception $e) {
97 if (php_sapi_name() != 'cli') {
98 header('HTTP/1.0 401 Unauthorized');
99 echo "<h1>Not authorized</h1>";
100 echo htmlentities($e->getMessage());
102 echo " ** Not authorized\n\n";
103 echo $e->getMessage() . "\n";
105 error_log($e->getMessage());
109 if (!count(self::$stack)) {
112 return self::$stack[0];
115 static function getUserClass($user = null) {
116 if ($user === null) {
117 $user = self::whoami();
119 if (MTrackConfig::get('core', 'admin_party') == 1
120 && $user == 'adminparty'
121 && ($_SERVER['REMOTE_ADDR'] == '127.0.0.1' ||
122 $_SERVER['REMOTE_ADDR'] == '::1')) {
126 $user_class = MTrackConfig::get('user_classes', $user);
127 if ($user_class === null) {
128 if ($user == 'anonymous') {
131 return 'authenticated';
136 static $userdata_cache = array();
137 static function getUserData($username) {
138 $username = mtrack_canon_username($username);
140 if (array_key_exists($username, self::$userdata_cache)) {
141 return self::$userdata_cache[$username];
144 foreach (self::$mechs as $mech) {
145 $data = $mech->getUserData($username);
146 if ($data !== null) {
150 if ($data === null) {
151 foreach (MTrackDB::q(
152 'select fullname, email from userinfo where userid = ?',
153 $username)->fetchAll(PDO::FETCH_ASSOC) as $row) {
158 if ($data === null) {
160 'fullname' => $username
164 if (!isset($data['email'])) {
165 if (preg_match('/<([a-z0-9_.+=-]+@[a-z0-9.-]+)>/', $username, $M)) {
166 // username contains an email address
167 $data['email'] = $M[1];
168 } else if (preg_match('/^([a-z0-9_.+=-]+@[a-z0-9.-]+)$/', $username)) {
169 // username is an email address
170 $data['email'] = $username;
171 } else if (preg_match('/^[a-z0-9_.+=-]+$/', $username)) {
172 // valid localpart; assume a domain and construct an email address
173 $dom = MTrackConfig::get('core', 'default_email_domain');
175 $data['email'] = $username . '@' . $dom;
180 self::$userdata_cache[$username] = $data;
185 /* enumerates possible groups from the auth plugin layer */
186 static function enumGroups() {
188 foreach (self::$mechs as $mech) {
189 $g = $mech->enumGroups();
191 foreach ($g as $i => $grp) {
192 if (is_integer($i)) {
193 $groups[$grp] = $grp;
200 /* merge in our project groups */
201 foreach (MTrackDB::q('select project, g.name, p.name from groups g left join projects p on g.project = p.projid')
203 $gid = "project:$row[0]:$row[1]";
204 $groups[$gid] = "$row[1] ($row[2])";
209 /* returns groups of which the authenticated user is a member */
210 static function getGroups($user = null) {
211 if ($user === null) {
212 $user = self::whoami();
214 $canon = mtrack_canon_username($user);
216 if (isset(self::$group_assoc[$user])) {
217 return self::$group_assoc[$user];
220 $roles = array($canon => $canon);
222 $user_class = self::getUserClass($user); // FIXME: $canon?
223 $class_roles = MTrackConfig::get('user_class_roles', $user_class);
224 foreach (preg_split('/\s*,\s*/', $class_roles) as $role) {
225 $roles[$role] = $role;
228 foreach (self::$mechs as $mech) {
229 $g = $mech->getGroups($user);
231 foreach ($g as $i => $grp) {
232 if (is_integer($i)) {
240 /* merge in our project group membership */
241 foreach (MTrackDB::q('select project, groupname, p.name from group_membership gm left join projects p on gm.project = p.projid where username = ?',
242 $canon)->fetchAll() as $row) {
243 $gid = "project:$row[0]:$row[1]";
244 $roles[$gid] = "$row[1] ($row[2])";
247 self::$group_assoc[$user] = $roles;
251 static function forceAuthenticate() {
253 $who = self::authenticate();
255 foreach (self::$mechs as $mech) {
256 $who = $mech->doAuthenticate(true);
265 } catch (Exception $e) {