Skip to content

Commit

Permalink
Merge pull request #28 from Ente/TT-5
Browse files Browse the repository at this point in the history
TT-5: Implement numeric code to clock in
  • Loading branch information
Ente authored Nov 29, 2024
2 parents 9b4f039 + 4df5fe7 commit dd63f8f
Show file tree
Hide file tree
Showing 16 changed files with 481 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* **Replaced `app.ini` with `app.json`**. The `app.ini` has been deprecated and will be removed within the `8.0` release. Your settings will be automatically migrated to the new `app.json` file.
* Added plugin to clock in with QR codes. This plugin is disabled by default and can be enabled within the `plugins.yml`. More information can be found inside the `README.md`.
* You can now register or remove a custom API route via the `CustomRoutes::registerCustomRoute(...)` or `CustomRoutes::removeCustomRoute(...)` functions. More information can be found inside the Toil API `/api/v1/toil/README.md`.
* Added a plugin to clock in with a code. This plugin is disabled by default and can be enabled within the `plugins.yml`. More information can be found inside the `README.md`.

## v7.5

Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,13 @@ When you have done this, you just have to enable the plugin by setting `enabled`

The link to `phpqrcode` also contains a wiki if you want to modify the plugin.

## CodeClock Plugin

This plugin allows you to clock in or out using a PIN to authenticate. The plugin is disabled by default and must be enabled in the `plugin.yml`.
You can access the plugin by navigating to `Plugins` -> `[codeclock] View PIN`. Admins can reset PINs through the `Plugins` -> `[codeclock] Admin View` page. You must have once accessed the plugin to let it generate the PINs.

To login with the PIN navigate to http://BASE_URL/api/v1/toil/code and enter your PIN.

## Updates

TimeTrack has to be updated in two ways: database and application.
Expand Down
13 changes: 13 additions & 0 deletions api/v1/class/plugins/plugins/codeclock/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# CodeClock Plugin

This plugin allows you to clock in or out using a PIN to authenticate. The plugin is disabled by default and must be enabled in the `plugin.yml`.

## Usage

### View PIN

You can view your PIN by navigating to `Plugins` -> `[codeclock] View PIN`.

### Resetting PIN

Admins can reset only all PINs through the `Plugins` -> `[codeclock] Admin View` page.
Empty file.
Empty file.
15 changes: 15 additions & 0 deletions api/v1/class/plugins/plugins/codeclock/plugin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: codeclock
src: /src
main: Main
namespace: CodeClock
author: Ente
description: Clock in to work with a PIN
version: "1.0"
api: 0.1
permissions: none
enabled: true
custom.values:
license: "LIC"
nav_links:
View PIN: views/index.php
Admin View: views/admin.php
51 changes: 51 additions & 0 deletions api/v1/class/plugins/plugins/codeclock/src/Code.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace CodeClock;
use Arbeitszeit\Arbeitszeit;
use Arbeitszeit\Benutzer;
use Arbeitszeit\PluginBuilder;
use Arbeitszeit\PluginInterface;
class Code extends codeclock {

public function readPINfile($username): string{
$file = dirname(__DIR__) . "/data/pin/{$username}";
if(file_exists($file)){
return file_get_contents($file);
} else {
return "";
}
}

public function validatePIN($username, $pin): bool{
$file = dirname(__DIR__) . "/data/pin/{$username}";
if(file_exists($file)){
$file_pin = file_get_contents($file);
try {
return password_verify($pin, $file_pin);
} catch (\Throwable $e){
return false;
}
} else {
return false;
}
}

public function getUserPIN($username): string{
$file = dirname(__DIR__, 1) . "/data/pin/{$username}_c";
if(file_exists($file)){
return file_get_contents($file);
} else {
return "";
}
}

public function getUserbyPIN($pin): string{
$files = glob(dirname(__DIR__, 1) . "/data/pin/*_c");
foreach($files as $file){
if(file_get_contents($file) == $pin){
return basename($file, "_c");
}
}
return "";
}
}
62 changes: 62 additions & 0 deletions api/v1/class/plugins/plugins/codeclock/src/Main.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace CodeClock;
use Arbeitszeit\Arbeitszeit;
use Arbeitszeit\Benutzer;
use Arbeitszeit\PluginBuilder;
use Arbeitszeit\PluginInterface;
use Toil\CustomRoutes;
class codeclock extends PluginBuilder implements PluginInterface {

public string $log_append;

private array $plugin_configuration;

private array $default_payload = [
"id" => null,
"username" => null,
"pin" => null,
];

private string $mastertoken;

public $setup;

public $code;

public function set_log_append(): void{
$v = $this->read_plugin_configuration("codeclock")["version"];
$this->log_append = "[codeclock v{$v}]";
}

public function get_log_append(): string{
return $this->log_append;
}

public function set_plugin_configuration(): void{
$this->plugin_configuration = $this->read_plugin_configuration("codeclock");
}

public function get_plugin_configuration(): array{
return $this->plugin_configuration;
}

public function __construct(){

#$this->code = new Code();
#$this->setup = new Setup();

Setup::done();
}

public function onDisable(): void{
$this->log_append = $this->get_log_append();

}

public function onEnable(): void{
}

public function onLoad(): void{
}
}
48 changes: 48 additions & 0 deletions api/v1/class/plugins/plugins/codeclock/src/Setup.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace CodeClock;
use Arbeitszeit\Arbeitszeit;
use Arbeitszeit\Benutzer;
use Arbeitszeit\PluginBuilder;
use Arbeitszeit\PluginInterface;
use Arbeitszeit\Exceptions;
use Toil\Toil;
use Toil\CustomRoutes;
class Setup extends codeclock {
public static function done(): bool{
$arbeit = new Arbeitszeit();
if(!file_exists(dirname(__DIR__) . "/data/token")){
Exceptions::error_rep(" No token file found, creating one now...");
$masterToken = bin2hex(random_bytes(64));

if(!file_put_contents(dirname(__DIR__) . "/data/token", $masterToken)){
Exceptions::failure("Could not create token file", "ERROR-PLUGIN-SETUP-CREATE-TOKEN", "N/A");
return false;
}

chmod(dirname(__DIR__) . "/data/token", 0600);

// create PIN 4-numeric file for each user
$users = $arbeit->benutzer()->get_all_users();
foreach($users as $user){
$pinString = (string)random_int(1000, 9999);
file_put_contents(dirname(__DIR__) . "/data/pin/{$user["username"]}_c", $pinString);
$pin = password_hash($pinString, PASSWORD_DEFAULT);
if(!file_put_contents(dirname(__DIR__) . "/data/pin/{$user["username"]}", $pin)){
Exceptions::failure("Could not create PIN file for user {$user["username"]}", "ERROR-PLUGIN-SETUP-CREATE-PIN", $user["id"]);
return false;
}
chmod(dirname(__DIR__) . "/data/pin/{$user["username"]}", 0600);
}

Exceptions::error_rep("Token file created successfully");

// register route
CustomRoutes::registerCustomRoute("code", "/api/v1/class/plugins/plugins/codeclock/views/routes/Code.ep.toil.arbeit.inc.php", 0);
return true;
} else {
Exceptions::error_rep("Token file found");
return true;
}
}
}
33 changes: 33 additions & 0 deletions api/v1/class/plugins/plugins/codeclock/views/admin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
require_once $_SERVER["DOCUMENT_ROOT"] . "/vendor/autoload.php";
require_once $_SERVER["DOCUMENT_ROOT"] . "/api/v1/inc/arbeit.inc.php";
require_once $_SERVER["DOCUMENT_ROOT"] . "/api/v1/class/plugins/loader.plugins.arbeit.inc.php";
require_once dirname(__DIR__, 1) . "/src/Main.php";
require_once dirname(__DIR__, 1) . "/src/Code.php";
require_once dirname(__DIR__, 1) . "/src/Setup.php";

use Arbeitszeit\Arbeitszeit;
use CodeClock\codeclock;
use CodeClock\Code;
use CodeClock\Setup;

$arbeit = new Arbeitszeit;
$main = new codeclock;
$code = new Code;
$setup = new Setup;
$message = "Ready to reset all PINs?";
if(isset($_GET["reset"])) {
//remove token file
unlink(dirname(__DIR__, 1) . "/data/token");
if($setup->done()){
$message = "PINs have been reset.";
}
}
?>
<div id="plugin-codeclock">
<h2>CodeClock Plugin - Reset PINs</h2>
<p>After clicking on reset all PINs will be regenerated.</p>
<p>Message: <span><?php echo $message; ?></span></p>
<a href="?pn=codeclock&p_view=views/admin.php&reset=true" class="button">Reset All PINs</a>

</div>
24 changes: 24 additions & 0 deletions api/v1/class/plugins/plugins/codeclock/views/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php
require_once $_SERVER["DOCUMENT_ROOT"] . "/vendor/autoload.php";
require_once $_SERVER["DOCUMENT_ROOT"] . "/api/v1/inc/arbeit.inc.php";
require_once $_SERVER["DOCUMENT_ROOT"] . "/api/v1/class/plugins/loader.plugins.arbeit.inc.php";
require_once dirname(__DIR__, 1) . "/src/Main.php";
require_once dirname(__DIR__, 1) . "/src/Code.php";
require_once dirname(__DIR__, 1) . "/src/Setup.php";

use Arbeitszeit\Arbeitszeit;
use CodeClock\codeclock;
use CodeClock\Code;

$arbeit = new Arbeitszeit;
$main = new codeclock;
$code = new Code;
?>
<div id="plugin-codeclock">
<h2>CodeClock Plugin</h2>
<p>Please view your PIN from below. You can then login from <a href="<?php echo "http://" . $arbeit->get_app_ini()["general"]["base_url"] . "/api/v1/toil/code" ?>">here</a></p>
<p>After login you can clock in or out.</p>

<br>
<p>PIN: <span id="pin"><?php echo $code->getUserPIN($_SESSION["username"]) ?></span></p>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php
namespace Toil;
require_once $_SERVER["DOCUMENT_ROOT"] . "/api/v1/inc/arbeit.inc.php";
use Toil\EP;
use Arbeitszeit\Arbeitszeit;
use Arbeitszeit\Auth;
use Arbeitszeit\Benutzer;
use Arbeitszeit\Projects;
class Code implements EPInterface {
public function get(){
require_once __DIR__ . "/code.php";
}

public function post($post = null){
require_once __DIR__ . "/code.php";
}

public function put(){
return true;
}

public function delete(){
return true;
}

public function __construct(){
return true;
}

public function __set($name, $value){
$this->$name = $value;
}

public function __get($name){
return $this->$name;
}

}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit dd63f8f

Please sign in to comment.