diff --git a/langs/en/actions.json b/langs/en/actions.json new file mode 100644 index 0000000..c03ccd4 --- /dev/null +++ b/langs/en/actions.json @@ -0,0 +1,5 @@ +{ + "Edit": "Edit", + "cancel": "Cancel", + "Choose a user": "Choose a user" +} diff --git a/langs/en/strings.json b/langs/en/strings.json new file mode 100644 index 0000000..f7a3f7f --- /dev/null +++ b/langs/en/strings.json @@ -0,0 +1,127 @@ +{ + "sign in": "Sign In", + "username": "Username", + "password": "Password", + "continue": "Continue", + "authcode": "Authentication code", + "2fa prompt": "Enter the six-digit code from your mobile authenticator app.", + "2fa incorrect": "Authentication code incorrect.", + "login incorrect": "Login incorrect.", + "no admin permission": "You do not have permission to access this system.", + "account locked": "This account has been disabled. Contact technical support.", + "password expired": "You must change your password before continuing.", + "account terminated": "Account terminated. Access denied.", + "account state error": "Your account state is not stable. Log out, restart your browser, and try again.", + "welcome user": "Welcome, {user}!", + "settings": "Settings", + "options": "Options", + "login server user data error": "The login server refused to provide account information. Try again or contact technical support.", + "captcha error": "There was a problem with the CAPTCHA (robot test). Try again.", + "home": "Home", + "users": "Users", + "more": "More", + "actions": "Actions", + "name": "Name", + "email": "Email", + "status": "Status", + "type": "Type", + "new user": "New User", + "total users": "Total Users", + "view users": "View Users", + "normal accounts": "Normal Accounts", + "locked accounts": "Locked Accounts", + "editing user": "Editing {user}", + "invalid userid": "Invalid user ID.", + "user saved": "User saved.", + "adding user": "Adding new user", + "placeholder name": "John Doe", + "placeholder username": "jdoe", + "placeholder email address": "jdoe@example.com", + "placeholder password": "swordfish", + "new password": "New Password", + "non-local account warning": "This account is not locally managed. Changes made here will not synchronize to the directory server and some attributes cannot be edited.", + "delete user": "Delete User", + "really delete user": "Are you sure you want to delete this user? This action cannot be reversed.", + "user deleted": "User account deleted.", + "user does not exist": "User does not exist.", + "logtime": "Date\/Time", + "logtype": "Event Type", + "ip address": "IP Address", + "other data": "Other", + "security log": "Security Log", + "event type reference": "Event Type Reference", + "clear log": "Clear Log", + "really clear log": "Are you sure you want to purge the security log? This action cannot be reversed.", + "log cleared": "Security log cleared.", + "removed n entries": "Removed {n} entries", + "security log entries": "Security Log Entries", + "view security log": "View Security Log", + "managers": "Managers", + "manager": "Manager", + "employee": "Employee", + "delete relationship": "Delete Relationship", + "really delete relationship": "Are you sure you want to remove this manager-employee relationship? This action cannot be reversed.", + "relationship deleted": "Relationship deleted.", + "edit relationship": "Edit Relationship", + "adding relationship": "Adding Relationship", + "relationship added": "Relationship added.", + "permissions": "Permissions", + "permission": "Permission", + "new permission": "New Permission", + "delete permission": "Delete Permission", + "adding permission": "Adding Permission", + "user": "User", + "permission does not exist": "Permission does not exist: {arg}", + "really delete permission": "Are you sure you want to revoke this permission?", + "permission added": "Permission assigned.", + "permission deleted": "Permission deleted.", + "remove 2fa": "Reset 2FA", + "action performed by": "Action performed by {user}", + "2fa removed": "2-factor authentication removed.", + "2fa": "2FA", + "show deleted": "Show deleted", + "editing deleted account": "You are editing an account marked as deleted. The account will be undeleted if you press Save.", + "manager assigned": "Manager relationships saved.", + "manager does not exist": "The selected manager username does not exist.", + "type to add a person": "Type to add a person", + "employees": "Employees", + "type to select a manager": "Type to select a manager", + "select a manager to view or edit employees": "Select a manager to view or edit the assigned employees.", + "report export": "Reports\/Export", + "report type": "Report type", + "format": "Format", + "generate report": "Generate report", + "choose an option": "Choose an option", + "csv file": "CSV text file", + "ods file": "ODS spreadsheet", + "html file": "HTML web page", + "uid": "User ID", + "manager name": "Manager", + "manager username": "Mgr. Username", + "employee name": "Employee", + "employee username": "Emp. Username", + "permission id": "Perm. ID", + "permissions assigned": "Permissions assigned.", + "type to select a user": "Type to select a user", + "type to add a permission": "Type to add a permission", + "Choose a permission": "Choose a permission", + "select a user to view or edit permissions": "Select a user to view or edit the assigned permissions.", + "group": "Group", + "groups": "Groups", + "group does not exist": "That group does not exist.", + "group members updated": "Group members updated.", + "group added": "Group added.", + "group deleted": "Group deleted.", + "group already exists": "A group with that name already exists.", + "save": "Save", + "next": "Next", + "add": "Add", + "delete": "Delete", + "new group": "New group", + "delete group": "Delete group", + "enter group name": "Group name", + "group management": "Group Management", + "group assignments": "Group Assignments", + "group id": "Group ID", + "group name": "Group Name" +} \ No newline at end of file diff --git a/lib/Log.lib.php b/lib/Log.lib.php new file mode 100644 index 0000000..15d8bb3 --- /dev/null +++ b/lib/Log.lib.php @@ -0,0 +1,72 @@ +getType(); + } + + if (is_a($user, "User")) { + $uid = $user->getUID(); + } else if (gettype($user) == "integer") { + $uid = $user; + } else { + $uid = null; + } + + $database->insert("authlog", ['logtime' => date("Y-m-d H:i:s"), 'logtype' => $type, 'uid' => $uid, 'ip' => $ip, 'otherdata' => $data]); + } + +} + +class LogType { + + const LOGIN_OK = 1; + const LOGIN_FAILED = 2; + const PASSWORD_CHANGED = 3; + const API_LOGIN_OK = 4; + const API_LOGIN_FAILED = 5; + const BAD_2FA = 6; + const API_BAD_2FA = 7; + const BAD_CAPTCHA = 8; + const ADDED_2FA = 9; + const REMOVED_2FA = 10; + const LOGOUT = 11; + const API_AUTH_OK = 12; + const API_AUTH_FAILED = 13; + const API_BAD_KEY = 14; + const LOG_CLEARED = 15; + const USER_REMOVED = 16; + const USER_ADDED = 17; + const USER_EDITED = 18; + const MOBILE_LOGIN_OK = 19; + const MOBILE_LOGIN_FAILED = 20; + const MOBILE_BAD_KEY = 21; + + private $type; + + function __construct(int $type) { + $this->type = $type; + } + + public function getType(): int { + return $type; + } +} diff --git a/lib/Report.lib.php b/lib/Report.lib.php new file mode 100644 index 0000000..73f1305 --- /dev/null +++ b/lib/Report.lib.php @@ -0,0 +1,137 @@ +title = $title; + $this->header = $header; + $this->data = $data; + } + + public function setHeader(array $header) { + $this->header = $header; + } + + public function addDataRow(array $columns) { + $this->data[] = $columns; + } + + public function getHeader(): array { + return $this->header; + } + + public function getData(): array { + return $this->data; + } + + public function output(string $format) { + switch ($format) { + case "ods": + $this->toODS(); + break; + case "html": + $this->toHTML(); + break; + case "csv": + default: + $this->toCSV(); + break; + } + } + + private function toODS() { + $ods = new ods(); + $styleColumn = new odsStyleTableColumn(); + $styleColumn->setUseOptimalColumnWidth(true); + $headerstyle = new odsStyleTableCell(); + $headerstyle->setFontWeight("bold"); + $table = new odsTable($this->title); + + for ($i = 0; $i < count($this->header); $i++) { + $table->addTableColumn(new odsTableColumn($styleColumn)); + } + + $row = new odsTableRow(); + foreach ($this->header as $cell) { + $row->addCell(new odsTableCellString($cell, $headerstyle)); + } + $table->addRow($row); + + foreach ($this->data as $cols) { + $row = new odsTableRow(); + foreach ($cols as $cell) { + $row->addCell(new odsTableCellString($cell)); + } + $table->addRow($row); + } + $ods->addTable($table); + // The @ is a workaround to silence the tempnam notice, + // which breaks the file. This is apparently the intended behavior: + // https://bugs.php.net/bug.php?id=69489 + @$ods->downloadOdsFile($this->title . "_" . date("Y-m-d_Hi") . ".ods"); + } + + private function toHTML() { + global $SECURE_NONCE; + $data = array_merge([$this->header], $this->data); + // HTML exporter doesn't like null values + for ($i = 0; $i < count($data); $i++) { + for ($j = 0; $j < count($data[$i]); $j++) { + if (is_null($data[$i][$j])) { + $data[$i][$j] = ''; + } + } + } + header('Content-type: text/html'); + $converter = new HTMLConverter(); + $out = "\n" + . "\n" + . "\n" + . "