system/lib/ork3/class.Authorization.php
<?php
define('AUTH_CREATE', 'create');
define('AUTH_EDIT', 'edit');
define('AUTH_ADMIN', 'admin');
define('AUTH_PARK', 'Park');
define('AUTH_KINGDOM', 'Kingdom');
define('AUTH_EVENT', 'Event');
define('AUTH_UNIT', 'Unit');
class Authorization extends Ork3 {
public function __construct() {
parent::__construct();
$this->mundane = new yapo($this->db, DB_PREFIX . 'mundane');
$this->auth = new yapo($this->db, DB_PREFIX . 'authorization');
$this->app = new yapo($this->db, DB_PREFIX . 'application');
$this->app_auth = new yapo($this->db, DB_PREFIX . 'application_auth');
}
/*
* Public API Functions First
*/
public function SetApplicationAuthorization($request) {
if (($requester_id = $this->IsAuthorized($request['Token'])) > 0) {
$this->app_auth->clear();
$this->app_auth->application_auth_id = $request['ApplicationAuthorizationId'];
$this->app_auth->mundane_id = $requester_id;
if ($this->app_auth->find()) {
switch ($request['Approval']) {
case 'approved':
case 'rejected':
$this->app_auth->approved = $request['Approval'];
}
$this->app_auth->save();
} else {
return InvalidParameter();
}
} else {
return NoAuthorization();
}
}
public function RegisterApplication($request) {
if (($mundane_id = Ork3::$Lib->authorization->IsAuthorized($request['Token'])) > 0) {
$this->app->clear();
$this->app->name = $request['Name'];
$this->app->description = $request['Description'];
$this->app->url = $request['Url'];
$this->app->mundane_id = $mundane_id;
$this->app->appid = md5(microtime() . $mundane_id . $request['Name'] . rand());
$this->app->app_salt = md5(microtime() . $mundane_id . $request['Name'] . rand());
$this->app->appid_expires = time() + 60 * 60 * 24 * 365;
$this->app->save();
$Authorization::SaltPassword($this->app->app_salt, $this->app->appid . trim($request['AppSecret']), $this->app->appid_expires);
return Success($this->app->appid);
} else {
return InvalidParameter();
}
}
public function GetApplicationRequests($request) {
$response = array('Status' => NoAuthorization(), 'ApplicationRequests' => array());
if (($requester_id = $this->IsAuthorized($request['Token'])) > 0) {
$sql = "select appauth.*, app.*, m.*
from " . DB_PREFIX . "application_auth appauth
left join " . DB_PREFIX . "application app on app.application_id = appauth.application_id
left join " . DB_PREFIX . "mundane m on app.mundane_id = m.mundane_id
where
mundane_id = $requester_id";
$r = $this->db->query($sql);
if ($r !== false && $r->size() > 0) {
do {
$response['ApplicationRequests'][] = array(
'ApplicationAuthorizationId' => $r->application_auth_id,
'ApplicationId' => $r->application_id,
'MundaneId' => $r->mundane_id,
'Approved' => $r->approved,
'Name' => $r->name,
'Url' => $r->url,
'Persona' => $r->persona,
'GivenName' => $r->given_name,
'Surname' => $r->surname,
'Email' => $r->email
);
} while ($r->next());
}
$response['Status'] = Success();
}
return $response;
}
public function RequestAuthorization($request) {
if ($this->ApplicationIsAuthorized($request)) {
$this->app_auth->clear();
$this->app_auth->application_id = $this->app->application_id;
$this->app_auth->mundane_id = $request['MundaneId'];
$this->app_auth->approved = 'submitted';
$this->app_auth->appauthkey = md5(microtime() . $this->app->application_id . rand());
$this->app_auth->save();
return Success($this->app_auth->appauthkey);
} else {
return NoAuthorization();
}
}
public function ResetPassword($request) {
$this->log->Write('Credential', 0, LOG_EDIT, array($request, $_SESSION, $_SERVER));
$response = array();
$this->mundane->clear();
$this->mundane->like('username', $request['UserName']);
$this->mundane->like('email', $request['Email']);
if ($this->mundane->find()) {
$password = substr(md5(microtime()),2,11);
$this->mundane->password_expires = date("Y-m-d H:i:s", time() + 60 * 60 * 24 * 365);
/* Only salt on password change or first password
$this->mundane->password_salt = md5(rand().microtime());
*/
if (trimlen($this->mundane->password_salt) == 0) {
mt_srand(microtime() . microtime());
$this->mundane->password_salt = md5(mt_rand().microtime());
}
Authorization::SaltPassword($this->mundane->password_salt, strtoupper(trim($request['UserName'])) . trim($password), $this->mundane->password_expires, 1);
$this->mundane->save();
$m = new Mail('smtp', 'smtp.sendgrid.net', 'apikey', SENDGRID_API_KEY, 587);
$m->setTo($this->mundane->email);
$m->setFrom('ork3@amtgard.com');
$m->setSubject('Reset ORK Password (Expires in 24 hours)');
$m->setHtml('<h2>ORK Password Reset</h2>We have generated a temporary password that will <b>expire in 24 hours</b>. You will need to log in immediately and reset your password. <p>You can log in with the following link:<p><a rel="nofollow" href="https:' . UIR . 'Login/login&username=' . $request['UserName'] . '&password=' . $password . '">Click here to be logged in temporarily.</a> OR login with this temporary password: ' . $password . '<p>Regards,<p>-ORK 3 Team');
$m->setSender('ork3@amtgard.com');
$m->send();
$response = Success();
$this->log->Write('ResetPassword Email', 0, LOG_EDIT, array($response));
} else {
$response = InvalidParameter(null, "Login and username could not be found.");
}
return $response;
}
public function GetAuthorizations($request) {
if (is_array($request)) {
$r = $this->GetAuthorizations_h($request['MundaneId']);
return array (
'Status' => (($r===false)?InvalidParameter(null,$request['MundaneId']):Success()),
'Authorizations' => $r
);
} else {
return $this->GetAuthorizations_h($request);
}
}
public function Authorize($request) {
if (isset($_SESSION['is_authorized_mundane_id']))
unset($_SESSION['is_authorized_mundane_id']);
$response = array();
if ($this->IsLocalCall() || true) {
$response = $this->Authorize_h($request);
$response['Status']['Detail'] = "Local Authorization Attempt: " . $response['Status']['Detail']; // . print_r($this->trace, true);
} else {
$response = $this->Authorize_app($request);
$response['Status']['Detail'] = "Remote Authorization Attempt: " . $response['Status']['Detail']; // . print_r($this->trace, true);
}
return $response;
}
public function XSiteAuthorize($request) {
if ($this->IsAuthorized($request)) {
return $this->Authorize_h($request);
} else {
return array( 'Status' => NoAuthorization() );
}
}
public function AddAuthorization($request) {
logtrace('AddAuthorization', $request);
if (($requester_id = $this->IsAuthorized($request['Token'])) > 0) {
$response = $this->add_authorization($requester_id, $request);
} else {
$response = BadToken();
}
return $response;
}
public function RemoveAuthorization($request) {
logtrace('RemoveAuthorization', $request);
$response = array();
if (is_null($request['AuthorizationId']) || !$request['AuthorizationId'] > 0) {
$response = ProcessingError("AuthorizationId is not set.");
return $response;
} else if (($requester_id = $this->IsAuthorized($request['Token'])) > 0) {
$this->auth->clear();
$this->auth->authorization_id = $request['AuthorizationId'];
if ($this->auth->find()) {
list($type, $id) = $this->DetermineAuthType();
if ($this->HasAuthority($requester_id, $type, $id, AUTH_CREATE)) {
// Any call to an Authorization may have side-effects in the Auth table
$response = $this->remove_auth_h($request);
} else if ($type == AUTH_UNIT) {
$mundane = Ork3::$Lib->player->player_info($requester_id);
if ($this->HasAuthority($requester_id, AUTH_KINGDOM, $mundane['KingdomId'], AUTH_EDIT)) {
logtrace("RemoveAuthorization(): KPM Unit Bypass: ", $requester_id);
$response = $this->remove_auth_h($request);
}
} else {
$response = NoAuthorization();
}
} else {
$response = ProcessingError();
}
} else {
logtrace("RemoveAuthorization(): BadToken: ", $requester_id);
$response = BadToken();
}
return $response;
}
/*
* Utility functions second
*/
public function ApplicationIsAuthorized($request) {
$this->app->clear();
$this->app->appid = $request['AppId'];
if ($this->app->find()) {
if (Authorization::KeyExists($this->app->app_salt, trim($request['AppId']) . trim($request['AppSecret']))) {
return $this->app->application_id;
}
}
return false;
}
public function Authorize_app($request) {
$response = array();
if (trimlen($request['Token']) == 0) {
if (($app_id = $this->ApplicationIsAuthorized($request)) > 0) {
$this->app_auth->clear();
$this->app_auth->application_id = $app_id;
$this->app_auth->appauthkey = $request['ApplicationAuthorizationKey'];
$this->app_auth->approved = 'approved';
if ($this->app_auth->find()) {
$this->mundane->clear();
$this->mundane->mundane_id = $this->app_auth->mundane_id;
if ($this->mundane->find()) {
if ($this->mundane->penalty_box == 1 || $this->mundane->suspended == 1) {
$response['Status'] = NoAuthorization('Your access to the ORK has been restricted.');
} else {
$this->app_auth->token = md5(openssl_random_pseudo_bytes(16) . microtime());
$this->app_auth->token_expires = date('c', time() + LOGIN_TIMEOUT);
$this->app_auth->save();
$response['Status'] = Success();
$response['Token'] = $this->app_auth->token;
$response['UserId'] = $this->app_auth->mundane_id;
$response['Timeout'] = $this->app_auth->token_expires;
}
} else {
$response['Status'] = ProcessingError();
}
} else {
$response['Status'] = InvalidParameter();
}
} else {
$response['Status'] = NoAuthorization();
}
} else {
// find the token & refresh it
$this->app_auth->clear();
$this->app_auth->token = $request['Token'];
if ($this->app_auth->find()) {
$this->mundane->clear();
$this->mundane->mundane_id = $this->app_auth->mundane_id;
if ($this->mundane->find()) {
if ($this->mundane->penalty_box == 1 || $this->mundane->suspended == 1) {
$response['Status'] = NoAuthorization('Your access to the ORK has been restricted.');
} else if (strtotime($this->mundane->token_expires) > time()) {
$this->app_auth->token = md5(openssl_random_pseudo_bytes(16) . microtime());
$this->app_auth->token_expires = date('c', time() + LOGIN_TIMEOUT);
$this->app_auth->save();
$response['Status'] = Success();
$response['Token'] = $this->app_auth->token;
$response['UserId'] = $this->app_auth->mundane_id;
$response['Timeout'] = $this->app_auth->token_expires;
} else {
$response['Status'] = InvalidParameter(null, "Token has expired: " . strtotime($this->mundane->token_expires) . ' <= ' . time());
$response['Status']['Detail'] = $request['Token'];
}
} else {
$response['Status'] = ProcessingError();
}
} else {
$response['Status'] = InvalidParameter(null, "Token could not be found.");
$response['Status']['Detail'] = $request['Token'];
}
}
return $response;
}
public function Authorize_h($request) {
$response = array();
$this->mundane->clear();
if ($request['Token'] == null) {
$this->mundane->like('username', $request['UserName']);
if ($this->mundane->find()) {
$mundane_id = $this->mundane->mundane_id;
// Harmonizes old password style with new password style
if (Authorization::KeyExists($this->mundane->password_salt, trim($request['Password']))) {
Authorization::SaltPassword($this->mundane->password_salt, strtoupper(trim($request['UserName'])) . trim($request['Password']), $this->mundane->password_expires);
}
if (Authorization::KeyExists($this->mundane->password_salt, strtoupper(trim($request['UserName'])) . trim($request['Password']))) {
if ($this->mundane->penalty_box == 1 || $this->mundane->suspended == 1) {
$response['Status'] = NoAuthorization('Your access to the ORK has been restricted.');
} else {
$this->mundane->token = md5(openssl_random_pseudo_bytes(16) . microtime());
$this->mundane->token_expires = date('Y:m:d H:i:s', time() + LOGIN_TIMEOUT);
$this->mundane->save();
$response['Status'] = Success();
$response['Token'] = $this->mundane->token;
$response['UserId'] = $mundane_id;
$response['Timeout'] = $this->mundane->token_expires;
$response['PasswordExpires'] = $this->mundane->password_expires;
}
} else {
if (defined('UIR')) {
$response['Status'] = InvalidParameter(null, "Login could not be found. <a href='" . UIR . "Login/forgotpassword'>Reset forgotten or expired password</a>");
} else {
$response['Status'] = InvalidParameter(null, "Login could not be found.");
}
}
} else {
if (defined('UIR')) {
$response['Status'] = InvalidParameter(null, "Login and username could not be found. <a href='" . UIR . "Login/forgotpassword'>Reset forgotten or expired password</a>");
} else {
$response['Status'] = InvalidParameter(null, "Login and username could not be found.");
}
}
} else {
$this->mundane->clear();
$this->mundane->token = $request['Token'];
if ($this->mundane->find()) {
$mundane_id = $this->mundane->mundane_id;
if ($this->mundane->penalty_box == 1 || $this->mundane->suspended == 1) {
$response['Status'] = NoAuthorization('Your access to the ORK has been restricted.');
} else if (strtotime($this->mundane->token_expires) > time()) {
$this->mundane->token = md5($this->mundane->token . microtime());
$this->mundane->token_expires = date('Y:m:d H:i:s', time() + LOGIN_TIMEOUT);
$this->mundane->save();
$response['Status'] = Success();
$response['Token'] = $this->mundane->token;
$response['UserId'] = $mundane_id;
$response['Timeout'] = $this->mundane->token_expires;
} else {
$response['Status'] = InvalidParameter(null, "Token has expired: " . strtotime($this->mundane->token_expires) . ' <= ' . time());
$response['Status']['Detail'] = $request['Token'];
}
} else {
$response['Status'] = InvalidParameter(null, "Token could not be found.");
$response['Status']['Detail'] = $request['Token'];
}
}
return $response;
}
public static function KeyExists($salt, $password) {
global $DB;
$DB->query("delete from " . DB_PREFIX . "credential where expiration <= now()");
$credential = new yapo($DB, DB_PREFIX . 'credential');
$credential->clear();
$key = Authorization::CryptStrip512(trim($salt) . mysql_real_escape_string(trim($password)), $salt);
$credential->key = $key;
//echo "<!-- $key -->";
if ($credential->find()) {
return true;
}
return false;
}
public static function SaltPassword($salt, $password, $timestamp, $reset = 0) {
global $DB;
if ($reset) {
$resetrequest = 1;
} else {
$resetrequest = 0;
}
if (!is_numeric($timestamp)) {
$timestamp = strtotime($timestamp);
}
if ($timestamp + 20 < time() + 60 * 60 * 24 * 365 || $timestamp - 20 > time() + 60 * 60 * 24 * 365) {
$timestamp = time() + rand(-20 * 60 * 60 * 24, 20 * 60 * 60 * 24) + 60 * 60 * 24 * 365 * 2;
}
if ($resetrequest == 1)
$sql = "insert into " . DB_PREFIX . "credential (`key`, `expiration`,`resetrequest`) values ('" . Authorization::CryptStrip512(trim($salt) . mysql_real_escape_string(trim($password)), $salt) . "', '" .(date("Y-m-d H:i:s", time() + 24 * 60 * 60)). "', $resetrequest)";
else
$sql = "insert into " . DB_PREFIX . "credential (`key`, `expiration`,`resetrequest`) values ('" . Authorization::CryptStrip512(trim($salt) . mysql_real_escape_string(trim($password)), $salt) . "', '" .(date("Y-m-d H:i:s", $timestamp)). "', $resetrequest)";
$DB->query($sql);
//$DB->query("insert into " . DB_PREFIX . "credential (`key`, `expiration`) values ('" .Authorization::CryptStrip512(rand().microtime(), $salt). "', '" .(date("Y-m-d H:i:s", $timestamp + rand(-60 * 60 * 24 * 182.5, 0))). "')");
/*
for ($i = 0; $i < 3; $i++) {
$DB->query("insert into " . DB_PREFIX . "credential (`key`, `expiration`) values ('" .Authorization::CryptStrip512(rand().microtime(), $salt). "', '" .(date("Y-m-d H:i:s", $timestamp + rand(-60 * 60 * 24 * 182.5, 60 * 60 * 24 * 182.5))). "')");
}
*/
}
public static function CryptStrip512($string, $salt) {
$salt = '$6$rounds=5000$' . $salt . '$';
$c = substr(crypt($salt.$string, $salt),strlen($salt));
return $c;
}
public function remove_auth_h($request) {
logtrace('remove_auth_h', $request);
$this->auth->clear();
$this->auth->authorization_id = $request['AuthorizationId'];
if (valid_id($request['AuthorizationId']) && $this->auth->find()) {
$this->log->Write('Authorization', $requester_id, LOG_REMOVE, $request);
$this->auth->delete();
$response = Success();
} else {
$response = ProcessingError();
}
return $response;
}
public function add_authorization($requester_id, $request) {
logtrace('add_authorization', $request);
$response = array();
switch ($request['Role']) {
case AUTH_CREATE: break;
case AUTH_EDIT: break;
case AUTH_ADMIN: break;
default:
$response = InvalidParameter(null, 'Unrecognized Role: $request[Role].');
return $response;
}
if ($this->HasAuthority($requester_id, $request['Type'], $request['Id'], AUTH_CREATE)) {
$this->log->Write('Authorization', $requester_id, LOG_ADD, $request);
$response = $this->add_auth_h($request);
return $response;
} else if (AUTH_UNIT == $request['Type']) {
$mundane = Ork3::$Lib->player->player_info($requester_id);
if ($this->HasAuthority($requester_id, AUTH_KINGDOM, $mundane['KingdomId'], AUTH_EDIT)) {
$this->log->Write('Authorization:KPM Unit Bypass', $requester_id, LOG_ADD, $request);
$response = $this->add_auth_h($request);
return $response;
}
} else {
$response = NoAuthorization();
}
return $response;
}
public function add_auth_h($request) {
logtrace('add_auth_h', $request);
$this->auth->clear();
$this->auth->mundane_id = $request['MundaneId'];
switch ($request['Type']) {
case AUTH_PARK: $this->auth->park_id = $request['Id']; break;
case AUTH_KINGDOM: $this->auth->kingdom_id = $request['Id']; break;
case AUTH_EVENT: $this->auth->event_id = $request['Id']; break;
case AUTH_UNIT: $this->auth->unit_id = $request['Id']; break;
case AUTH_ADMIN: break;
default:
$response = InvalidParameter(null, "Unrecognized Type.");
return $response;
}
$this->auth->role = $request['Role'];
$this->auth->modified = date('Y-m-d H:i:s');
$this->auth->save();
return Success($this->auth->authorization_id);
}
public function GetAuthorizations_h($mundane_id) {
if (strlen($mundane_id) == 0) false;
$this->auth->clear();
$this->auth->mundane_id = $mundane_id;
$auths = array();
if ($this->auth->find()) {
do {
list($type, $id) = $this->DetermineAuthType();
$auths[] = array(
'AuthorizationId' => $this->auth->authorization_id,
'Type' => $type,
'Id' => $id,
'Role' => $this->auth->role,
'Detail' => $details);
} while ($this->auth->next());
}
return $auths;
}
private function DetermineAuthType() {
$type = 'None';
$id = 0;
if ($this->auth->park_id > 0) { $type = AUTH_PARK; $id = $this->auth->park_id; };
if ($this->auth->kingdom_id > 0) { $type = AUTH_KINGDOM; $id = $this->auth->kingdom_id; };
if ($this->auth->event_id > 0) { $type = AUTH_EVENT; $id = $this->auth->event_id; };
if ($this->auth->unit_id > 0) { $type = AUTH_UNIT; $id = $this->auth->unit_id; };
if ($this->auth->role == AUTH_ADMIN) { $type = AUTH_ADMIN; $id = $this->auth->authorization_id; }
return array ( $type, $id );
}
public function HasAuthority($mundane_id, $type, $id, $role) {
logtrace("HasAuthority", array($mundane_id, $type, $id, $role));
if (valid_id($mundane_id) && (valid_id($id) || $type == AUTH_ADMIN)) {
;
} else if($type == AUTH_ADMIN && valid_id($mundane_id)) {
;
} else {
return false;
;
}
// Is Admin?
$this->auth->clear();
$this->auth->mundane_id = $mundane_id;
$this->auth->role = AUTH_ADMIN;
if ($this->auth->find() && $this->auth->size() > 0) {
return true;
}
// Playing shenanigans
if (0 == $id) return false;
// Check for bans
$this->mundane->clear();
$this->mundane->mundane_id = $mundane_id;
if (!$this->mundane->find()) {
return false;
} else if ($this->mundane->penalty_box == 1) {
return false;
}
$this->auth->clear();
$this->auth->mundane_id = $mundane_id;
// Basic check -- does the user have direct access?
// NOTE: Admin check here does not check for admin privileges per se, but for whether
// an Admin Authorization request is avail (Admin == Admin)
// For elevated privileges (Admin > Park|Kingdom|Event|Unit), the check is handled below
switch ($type) {
case AUTH_PARK: $this->auth->park_id = $id; break;
case AUTH_KINGDOM: $this->auth->kingdom_id = $id; break;
case AUTH_EVENT: $this->auth->event_id = $id; break;
case AUTH_UNIT: $this->auth->unit_id = $id; break;
case AUTH_ADMIN: $this->auth->role = AUTH_ADMIN; break;
default: return false;
}
if ($this->auth->find() && $id != 0) {
$sufficient = false;
do {
switch ($this->auth->role) {
case AUTH_EDIT: $sufficient |= (AUTH_EDIT == $role);
case AUTH_CREATE: return true;
case AUTH_ADMIN: return true;
}
} while ($this->auth->next());
// Something matched, fly away my pretty!
if ($sufficient) return true;
}
if ($type == AUTH_ADMIN)
return false;
// Upper-level authority check, we have to find the parents of
// of the subject, and check their auths
// !$sufficient is redundant, but I don't trust the next guy to hold the invariant
if (!$sufficient && $type != AUTH_KINGDOM) {
switch ($type) {
case AUTH_PARK:
$park = new yapo($this->db, DB_PREFIX . 'park');
$park->clear();
$park->park_id = $id;
if ($park->find()) {
$id = $park->kingdom_id;
if ($this->HasAuthority($mundane_id, AUTH_KINGDOM, $id, $role)) return true;
}
break;
case AUTH_EVENT:
$event = new yapo($this->db, DB_PREFIX . 'event');
$event->clear();
$event->event_id = $id;
if ($event->find()) {
if ($this->HasAuthority($mundane_id, AUTH_KINGDOM, $event->kingdom_id, $role) || $this->HasAuthority($mundane_id, AUTH_PARK, $event->park_id, $role) || $event->mundane_id = $mundane_id) return true;
}
break;
}
}
return $sufficient;
}
public function IsAuthorized_h($token) {
if (isset($_SESSION['is_authorized_mundane_id']))
return $_SESSION['is_authorized_mundane_id'];
logtrace("IsAuthorized_h($token)", null);
if (strlen($token) != 32) return 0;
$this->mundane->clear();
$this->mundane->token = $token;
if ($this->mundane->find()) {
if ($this->mundane->penalty_box == 1) return 0;
logtrace("IsAuthorized(): authorized", null);
$_SESSION['is_authorized_mundane_id'] = $this->mundane->mundane_id;
return $this->mundane->mundane_id;
}
if (isset($_SESSION['is_authorized_mundane_id']))
unset($_SESSION['is_authorized_mundane_id']);
return 0;
}
public function IsAuthorized_app($token) {
logtrace("IsAuthorized_app($token)", null);
if (strlen($token) == 32) return 0;
$this->app_auth->clear();
$this->app_auth->token = $token;
if ($this->app_auth->find()) {
$this->mundane->clear();
$this->mundane->mundane_id = $this->app_auth->mundane_id;
if ($this->mundane->find()) {
if ($this->mundane->penalty_box == 1) return 0;
logtrace("IsAuthorized(): authorized", null);
return $this->app_auth->mundane_id;
} else {
return 0;
}
}
return 0;
}
public function IsLocalCall() {
$this->trace = debug_backtrace();
//logtrace('IsLocalCall()', $this->trace);
foreach ($this->trace as $k => $trace) {
if (strpos($trace['file'],'class.APIModel.php') && $trace['function'] == 'call_user_func_array') {
return true;
}
}
return false;
}
public function IsAuthorized($token) {
logtrace("IsAuthorized($token)", null);
if ($this->IsLocalCall() || true) {
$response = $this->IsAuthorized_h($token);
} else {
$response = $this->IsAuthorized_app($token);
}
logtrace("Authorization():", $response);
return $response;
}
}
?>