/** * Generate a new atom identifier for this concept * @return string */ public function createNewAtomId() { static $prevTime = null; if (strpos($this->name, '_AI') !== false && $this->isInteger()) { $firstCol = current($this->mysqlConceptTable->getCols()); $query = "SELECT MAX(`{$firstCol->name}`) as `MAX` FROM `{$this->mysqlConceptTable->name}`"; $result = array_column((array) $this->database->Exe($query), 'MAX'); if (empty($result)) { $atomId = 1; } else { $atomId = $result[0] + 1; } } else { $now = explode(' ', microTime()); // yields ["microseconds", "seconds"] both in seconds, e.g. ["0.85629400", "1322761879"] $time = $now[1] . substr($now[0], 2, 6); // we drop the leading "0." and trailing "00" from the microseconds // Guarantee that time is increased if ($time <= $prevTime) { $time = ++$prevTime; } else { $prevTime = $time; } $atomId = $this->name . '_' . $time; } return $atomId; }
/** * * @throws Exception when segment type is unknown * @throws Exception when segment expression return more that 1 tgt atom * @return string */ public function getViolationMessage() { $database = Database::singleton(); $strArr = array(); foreach ($this->rule->violationSegments as $segment) { // text segment if ($segment['segmentType'] == 'Text') { $strArr[] = $segment['Text']; // expressie segment } elseif ($segment['segmentType'] == 'Exp') { // select starting atom depending on whether the segment uses the src of tgt atom. $atom = $segment['srcOrTgt'] == 'Src' ? $this->src : $this->tgt; // quering the expression $query = "SELECT DISTINCT `tgt` FROM ({$segment['expSQL']}) AS `results` WHERE `src` = '{$atom->idEsc}'"; // SRC of TGT kunnen door een expressie gevolgd worden $rows = $database->Exe($query); // returning the result if (count($rows) > 1) { throw new Exception("Expression of pairview results in more than one tgt atom", 501); } // 501: Not implemented $strArr[] = $rows[0]['tgt']; // unknown segment } else { $errorMessage = "Unknown segmentType '{$segment['segmentType']}' in violationSegments of rule '{$this->rule->id}'"; throw new Exception($errorMessage, 501); // 501: Not implemented } } // If empty array of strings (i.e. no violation segments defined), use default violation representation: '<srcAtom>,<tgtAtom>' $this->message = empty($strArr) ? "{$this->src->getLabel()},{$this->tgt->getLabel()}" : implode($strArr); return $this->message; }
/** * Function to delete an atom from Concept collection * @param array $options * @throws Exception when delete is not allowed/possible * @return void */ public function delete($options = array()) { $this->logger->debug("delete() called on {$this->path}"); // CRUD check if (!$this->parentIfc->crudD) { throw new Exception("Delete not allowed for '{$this->path}'", 405); } if (!$this->parentIfc->tgtConcept->isObject) { throw new Exception("Cannot delete non-object '{$this->__toString()}' in '{$this->path}'. Use PATCH remove operation instead", 405); } if ($this->parentIfc->isRef()) { throw new Exception("Cannot delete on reference interface in '{$this->path}'. See #498", 501); } // Handle options if (isset($options['requestType'])) { $this->database->setRequestType($options['requestType']); } // Perform delete $this->deleteAtom(); // Close transaction $this->database->closeTransaction($this->concept . ' deleted'); return; }
/** * Return array with all links (pair of Atoms) in this relation * @return array[] */ public function getAllLinks() { // Query all atoms in table $query = "SELECT `{$this->mysqlTable->srcCol()->name}` as `src`, `{$this->mysqlTable->tgtCol()->name}` as `tgt` FROM `{$this->mysqlTable->name}`"; return (array) $this->db->Exe($query); }
$allLinks = array(); foreach (Relation::getAllRelations() as $rel) { $allLinks[$rel->signature] = $rel->getAllLinks(); } $strFileContent = '<?php' . PHP_EOL . '$allAtoms = ' . var_export($allAtoms, true) . ';' . PHP_EOL . '$allLinks = ' . var_export($allLinks, true) . ';' . PHP_EOL . '?>'; file_put_contents(Config::get('absolutePath') . Config::get('logPath') . "export-" . date('Y-m-d_H-i-s') . ".php", $strFileContent); }); $app->get('/admin/import', function () use($app) { if (Config::get('productionEnv')) { throw new Exception("Import not allowed in production environment", 403); } $file = $app->request->params('file'); if (is_null($file)) { throw new Exception("Import file not specified", 500); } $database = Database::singleton(); include_once Config::get('absolutePath') . Config::get('logPath') . "{$file}"; // check if all concepts and relations are defined foreach ((array) $allAtoms as $cpt => $atoms) { if (!empty($atoms)) { Concept::getConcept($cpt); } } foreach ((array) $allLinks as $rel => $links) { if (!empty($links)) { Relation::getRelation($rel); } } foreach ((array) $allAtoms as $cpt => $atoms) { $concept = Concept::getConcept($cpt); foreach ($atoms as $atomId) {
/** * Function to create a new Atom at the given interface. * @param array $data * @param array $options * @throws Exception * @return mixed */ public function create($data, $options = array()) { $this->logger->debug("create() called on {$this->path}"); // CRUD check if (!$this->crudC) { throw new Exception("Create not allowed for '{$this->path}'", 405); } if (!$this->tgtConcept->isObject) { throw new Exception("Cannot create non-object '{$this->tgtConcept}' in '{$this->path}'. Use PATCH add operation instead", 405); } if ($this->isRef()) { throw new Exception("Cannot create on reference interface in '{$this->path}'. See #498", 501); } // Handle options if (isset($options['requestType'])) { $this->database->setRequestType($options['requestType']); } // Perform create $newAtom = $this->tgtConcept->createNewAtom(); // Special case for CREATE in I[Concept] interfaces if ($this->srcAtom->id === '_NEW') { $this->srcAtom->setId($newAtom->id); $this->path = str_replace('_NEW', $newAtom->getJsonRepresentation(), $this->path); } // If interface expression is a relation, also add tuple(this, newAtom) in this relation if ($this->relation) { $this->relation()->addLink($this->srcAtom, $newAtom, $this->relationIsFlipped); } else { $newAtom->addAtom(); } // Walk to new atom $newAtom = $this->atom($newAtom->id); // Set requested state (using patches) $patches = is_array($data) ? $data['patches'] : array(); $newAtom->doPatches($patches); // Special case for file upload. TODO: make extension with hooks if ($this->tgtConcept->isFileObject()) { $conceptFilePath = Concept::getConceptByLabel('FilePath'); $conceptFileName = Concept::getConceptByLabel('FileName'); if (is_uploaded_file($_FILES['file']['tmp_name'])) { $tmp_name = $_FILES['file']['tmp_name']; $new_name = time() . '_' . $_FILES['file']['name']; $absolutePath = Config::get('absolutePath') . Config::get('uploadPath') . $new_name; $relativePath = Config::get('uploadPath') . $new_name; $result = move_uploaded_file($tmp_name, $absolutePath); if ($result) { Logger::getUserLogger()->notice("File '{$new_name}' uploaded"); } else { throw new Exception("Error in file upload", 500); } // Populate filePath and originalFileName relations in database $relFilePath = Relation::getRelation('filePath', $newAtom->concept, $conceptFilePath); $relOriginalFileName = Relation::getRelation('originalFileName', $newAtom->concept, $conceptFileName); $relFilePath->addLink($newAtom, new Atom($relativePath, $conceptFilePath)); $relOriginalFileName->addLink($newAtom, new Atom($_FILES['file']['name'], $conceptFileName)); } else { throw new Exception("No file uploaded", 500); } } // Close transaction $this->database->closeTransaction($newAtom->concept . ' created', null, $newAtom); // temp store content of $newAtom (also when not crudR) // Return atom content (can be null) return $newAtom->getStoredContent(); }
private function login($email) { if (empty($email)) { throw new Exception("No emailaddress provided to login", 500); } $session = Session::singleton(); $db = Database::singleton(); $conceptUserID = Concept::getConceptByLabel('UserID'); $conceptDomain = Concept::getConceptByLabel('Domain'); $conceptDateTime = Concept::getConceptByLabel('DateTime'); $conceptOrg = Concept::getConceptByLabel('Organization'); $conceptAccount = Concept::getConceptByLabel('Account'); $conceptSession = Concept::getConceptByLabel('SESSION'); // Set sessionUser $atom = new Atom($email, $conceptUserID); $accounts = $atom->ifc('AccountForUserid')->getTgtAtoms(); // create new user if (empty($accounts)) { $newAccount = Concept::getConceptByLabel('Account')->createNewAtom(); // Save email as accUserid $relAccUserid = Relation::getRelation('accUserid', $newAccount->concept, $conceptUserID); $relAccUserid->addLink($newAccount, new Atom($email, $conceptUserID), false, 'OAuthLoginExtension'); // If possible, add account to organization(s) based on domain name $domain = explode('@', $email)[1]; $atom = new Atom($domain, $conceptDomain); $orgs = $atom->ifc('DomainOrgs')->getTgtAtoms(); $relAccOrg = Relation::getRelation('accOrg', $newAccount->concept, $conceptOrg); foreach ($orgs as $org) { $relAccOrg->addLink($newAccount, $org, false, 'OAuthLoginExtension'); } // Account created, add to $accounts list (used lateron) $accounts[] = $newAccount; } if (count($accounts) > 1) { throw new Exception("Multiple users registered with email {$email}", 401); } $relSessionAccount = Relation::getRelation('sessionAccount', $conceptSession, $conceptAccount); $relAccMostRecentLogin = Relation::getRelation('accMostRecentLogin', $conceptAccount, $conceptDateTime); $relAccLoginTimestamps = Relation::getRelation('accLoginTimestamps', $conceptAccount, $conceptDateTime); foreach ($accounts as $account) { // Set sessionAccount $relSessionAccount->addLink($session->sessionAtom, $account, false, 'OAuthLoginExtension'); // Timestamps $ts = new Atom(date(DATE_ISO8601), $conceptDateTime); $relAccMostRecentLogin->addLink($account, $ts, false, 'OAuthLoginExtension'); $relAccLoginTimestamps->addLink($account, $ts, false, 'OAuthLoginExtension'); } $db->closeTransaction('Login successfull', true); }
global $app; // Path to API is 'api/v1/excelimport/import' $app->post('/excelimport/import', function () use($app) { $session = Session::singleton(); $roleIds = $app->request->params('roleIds'); $session->activateRoles($roleIds); // Check sessionRoles if allowedRolesForExcelImport is specified $allowedRoles = Config::get('allowedRolesForExcelImport', 'excelImport'); if (!is_null($allowedRoles)) { $ok = false; foreach ($session->getSessionRoles() as $role) { if (in_array($role->label, $allowedRoles)) { $ok = true; } } if (!$ok) { throw new Exception("You do not have access to import excel files", 401); } } if (is_uploaded_file($_FILES['file']['tmp_name'])) { // Parse: $parser = new ExcelImport(); $parser->ParseFile($_FILES['file']['tmp_name']); Database::singleton()->closeTransaction("File {$_FILES['file']['tmp_name']} imported successfully", true); unlink($_FILES['file']['tmp_name']); } else { Logger::getUserLogger()->error("No file uploaded"); } $result = array('notifications' => Notifications::getAll(), 'files' => $_FILES); print json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); });
/** * Overwrites getViolationMessage() method from Violation class * @throws Exception when segment type is unknown * @throws Exception when segment expression return more that 1 tgt atom * @return string */ public function getViolationMessage() { $database = Database::singleton(); $strArr = array(); foreach ($this->rule->violationSegments as $segment) { // text segment if ($segment['segmentType'] == 'Text') { $strArr[] = $segment['Text']; // expressie segment } elseif ($segment['segmentType'] == 'Exp') { // select starting atom depending on whether the segment uses the src of tgt atom. $atom = $segment['srcOrTgt'] == 'Src' ? $this->src : $this->tgt; $rows = array(); if ($segment['expIsIdent']) { // when segment expression isIdent (i.e. SRC I or TGT I), we don't have to query the database. $rows[] = array('tgt' => $atom->id); } else { // quering the expression $query = "SELECT DISTINCT `tgt` FROM ({$segment['expSQL']}) AS `results` WHERE `src` = '{$atom->idEsc}'"; // SRC of TGT kunnen door een expressie gevolgd worden $rows = $database->Exe($query); } // returning the result if (count($rows) == 0) { $strArr[] = '_NULL'; } else { $str = ''; foreach ($rows as $row) { $str .= $row['tgt'] . '_AND'; } $str = substr($str, 0, -4); // strip the last _AND $strArr[] = str_replace(array('{EX}', '{php}'), '', $str); // prevent php interpreter by user input. Only allowed as Text segments specified in &-script } // unknown segment } else { $errorMessage = "Unknown segmentType '{$segment['segmentType']}' in violationSegments of rule '{$this->rule->id}'"; throw new Exception($errorMessage, 501); // 501: Not implemented } } return $this->message = implode($strArr); }
function OverwritePopulation($rArray, $relationName, $conceptName) { try { $database = Database::singleton(); $concept = Concept::getConceptByLabel($conceptName); $relation = Relation::getRelation($relationName, $concept, $concept); $relationTable = $relation->getMysqlTable(); $srcCol = $relationTable->srcCol(); $tgtCol = $relationTable->tgtCol(); $query = "DELETE FROM `{$relationTable->name}`"; // Do not use TRUNCATE statement, this causes an implicit commit $database->Exe($query); foreach ($rArray as $src => $tgtArray) { foreach ($tgtArray as $tgt => $bool) { if ($bool) { $query = "INSERT INTO `{$relationTable->name}` (`{$srcCol->name}`, `{$tgtCol->name}`) VALUES ('{$src}','{$tgt}')"; $database->Exe($query); } } } } catch (Exception $e) { throw new Exception('OverwritePopulation: ' . $e->getMessage(), 500); } }
/** * Function to evaluate conjunct * @param boolean $cacheConjuncts * @return array[] array(array('src' => '<srcAtomId>', 'tgt' => '<tgtAtomId>')) */ public function evaluateConjunct($cacheConjuncts = true) { $this->logger->debug("Checking conjunct '{$this->id}' cache:" . var_export($cacheConjuncts, true)); try { // If conjunct is already evaluated and conjunctCach may be used -> return violations if (isset($this->conjunctViolations) && $cacheConjuncts) { $this->logger->debug("Conjunct is already evaluated, getting violations from cache"); return $this->conjunctViolations; // Otherwise evaluate conjunct, cache and return violations } else { $db = Database::singleton(); $dbsignalTableName = Config::get('dbsignalTableName', 'mysqlDatabase'); $violations = array(); // Execute conjunct query $violations = (array) $db->Exe($this->query); // Cache violations in php Conjunct object if ($cacheConjuncts) { $this->conjunctViolations = $violations; } if (count($violations) == 0) { $this->logger->debug("Conjunct '{$this->id}' holds"); // Remove "old" conjunct violations from database $query = "DELETE FROM `{$dbsignalTableName}` WHERE `conjId` = '{$this->id}'"; $db->Exe($query); } else { $this->logger->debug("Conjunct '{$this->id}' broken, updating violations in database"); // Remove "old" conjunct violations from database $query = "DELETE FROM `{$dbsignalTableName}` WHERE `conjId` = '{$this->id}'"; $db->Exe($query); // Add new conjunct violation to database $query = "INSERT IGNORE INTO `{$dbsignalTableName}` (`conjId`, `src`, `tgt`) VALUES "; $values = array(); foreach ($violations as $violation) { $values[] = "('{$this->id}', '" . $db->escape($violation['src']) . "', '" . $db->escape($violation['tgt']) . "')"; } $query .= implode(',', $values); $db->Exe($query); } return $violations; } } catch (Exception $e) { Logger::getUserLogger()->error("While checking conjunct '{$this->id}': " . $e->getMessage()); return array(); } }
/** * Function to return the database instance * @return Database */ public static function singleton() { try { if (!is_object(self::$_instance)) { self::$_instance = new Database(); } } catch (Exception $e) { // Convert mysqli_sql_exceptions into 500 errors if (!Config::get('productionEnv')) { switch ($e->getCode()) { case 1049: // Error: 1049 SQLSTATE: 42000 (ER_BAD_DB_ERROR) // throw new Exception("Please <a href=\"#/admin/installer\" class=\"alert-link\">install database</a>",500); self::createDB(); self::$_instance = new Database(); self::$_instance->logger->info("Automatically installing database for the first time"); self::$_instance->reinstallDB(); break; default: throw new Exception("{$e->getCode()}: {$e->getMessage()}", 500); } } else { throw new Exception("Cannot connect to database", 500); } } return self::$_instance; }
function ClearConcept($concept, $atom) { Logger::getLogger('EXECENGINE')->info("ClearConcept({$concept},{$atom})"); if (func_num_args() != 2) { throw new Exception("Wrong number of arguments supplied for function ClearConcept(): " . func_num_args() . " arguments", 500); } try { $database = Database::singleton(); $atom = new Atom($atom, Concept::getConceptByLabel($concept)); $database->atomClearConcept($atom); Logger::getLogger('EXECENGINE')->debug("Atom '{$atom->__toString()}' removed as member from concept '{$concept}'"); } catch (Exception $e) { Logger::getUserLogger()->error('ClearConcept: ' . $e->getMessage()); } }
/** * Returns if session refresh is adviced in frontend * True when * - session variable is affected (otherwise nothing to update) * - AND transaction request is 'promise' (otherwise rollback) * - AND invariant rules hold (otherwise rollback) * False otherwise * @return boolean */ public function getSessionRefreshAdvice() { return $this->getSessionVarAffected() && $this->database->getRequestType() == 'promise' && $this->database->getInvariantRulesHold(); }