Skip to content

Commit

Permalink
feat: add user groups and group ACL (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
VictorWesterlund authored Mar 19, 2024
1 parent 3da4e95 commit 83c4d49
Show file tree
Hide file tree
Showing 25 changed files with 401 additions and 533 deletions.
42 changes: 14 additions & 28 deletions .env.example.ini
Original file line number Diff line number Diff line change
@@ -1,38 +1,24 @@
# --------------------------------------------
# # Your endpoints
# Absolute path to the root folder of your API
# --------------------------------------------
; --------------------------------------------
; # Your endpoints
; Absolute path to the root folder of your API
; --------------------------------------------

endpoints = ""

# --------------------------------------------------
# # Reflect database
# MySQL/MariaDB Credentials for the Reflect database
# --------------------------------------------------
; --------------------------------------------------
; # Reflect database
; MySQL/MariaDB Credentials for the Reflect database
; --------------------------------------------------

mysql_host = ""
mysql_user = ""
mysql_pass = ""
mysql_db = "reflect"

# ----------------------------------------------------
# # (Optional) UNIX Socket server
# Configuration for the optional Reflect socket server
# ----------------------------------------------------
; --------------------------------------------------------------------------------
; # Reflect internal endpoints prefix
; All requests starting with this prefix will be treated as an internal request.
; Internal requests are routed to src/api/reflect/*
; --------------------------------------------------------------------------------

## Absolute path to the socket file to be created when the Reflect socket server is started
socket = "/run/reflect/api.sock"
## Socket file octal permissions (chmod)
socket_mode = 01750

# ---------------------------------------------------------------------------
# # Optional features and settings
# Uncomment lines prefixed with ";" to enable and configure optional features
# ---------------------------------------------------------------------------

## Request idempotency
# This feature enforces idempotency on POST, PUT, and PATCH requests.
# - Read more at: https://github.com/victorwesterlund/reflect/wiki/idempotency
#
# Absolute path to the *directory* where an SQLite database for idempotency keys will be created.
;idempotency = "/var/lib/reflect/"
internal_request_prefix = "reflect/"
2 changes: 0 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
{
"require": {
"victorwesterlund/libmysqldriver": "^3.2",
"victorwesterlund/libsqlitedriver": "^1.0",
"victorwesterlund/xenum": "^1.1",
"reflect/plugin-rules": "^1.0"
}
}
100 changes: 13 additions & 87 deletions composer.lock

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

86 changes: 42 additions & 44 deletions src/Init.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,85 +7,83 @@
Everything here is loaded before endpoint request processing begins.
*/

/*
# Default endpoint interface
This interface need to be implemented by all endpoints
*/
interface Endpoint {
public function main();
}
use Reflect\ENV;
use Reflect\Path;

enum ENV: string {
protected const NAMESPACE = "_reflect";
protected const ENV_INI = ".env.ini";
protected const COMPOSER = "vendor/autoload.php";

/*
# Reflect environment abstractions
This class contains abstractions for Reflect environment variables
*/
class ENV {
// Reflect environment variables are placed in $_ENV as an assoc array with this as the array key.
// Example: $_ENV[self::NS][<reflect_env_var>]
private const NS = "_REFLECT";
// START User configurable environment variables

case ENDPOINTS = "endpoints";

case MYSQL_HOST = "mysql_host";
case MYSQL_USER = "mysql_user";
case MYSQL_PASS = "mysql_pass";
case MYSQL_DB = "mysql_db";

case INTERNAL_REQUEST_PREFIX = "internal_request_prefix";

// Name of the .ini file containing environment variables to be loaded (internal and userspace)
private const INI = ".env.ini";
// END User configurable environment variables

// Path to the composer autoload file (internal and userspace)
private const COMPOSER_AUTLOAD = "vendor/autoload.php";
case INTERNAL_STDOUT = "internal_stdout";

// Returns true if Reflect environment variable is present and not empty in
public static function isset(string $key): bool {
return in_array($key, array_keys($_ENV[self::NS])) && !empty($_ENV[self::NS][$key]);
}
// Returns true if Reflect environment variable is present and not empty in
public static function isset(ENV $key): bool {
return in_array($key->value, array_keys($_ENV[self::NAMESPACE])) && !empty($_ENV[self::NAMESPACE][$key->value]);
}

// Get environment variable by key
public static function get(string $key): mixed {
return self::isset($key) ? $_ENV[self::NS][$key] : null;
public static function get(ENV $key): mixed {
return self::isset($key) ? $_ENV[self::NAMESPACE][$key->value] : null;
}

// Set environment variable key, value pair
public static function set(string $key, mixed $value = null) {
$_ENV[self::NS][$key] = $value;
public static function set(ENV $key, mixed $value = null) {
$_ENV[self::NAMESPACE][$key->value] = $value;
}

/* ---- */

// Load environment variables and dependancies
public static function init() {
// Initialize namespaced environment variables from .ini config file
$_ENV[self::NS] = parse_ini_file(Path::reflect(self::INI), true) ?? die("Environment variable file '" . self::INI . "' not found");
// Put environment variables from Vegvisir .ini into namespaced superglobal
$_ENV[self::NAMESPACE] = parse_ini_file(Path::reflect(self::ENV_INI), true);

require_once Path::reflect(self::COMPOSER_AUTLOAD) ?? die("Failed to load dependencies. Install dependencies with 'composer install'");
// Don't perform loopback responses by default
ENV::set(ENV::INTERNAL_STDOUT, false);

// Merge environment variables from userspace if present
if (file_exists(Path::root(self::INI))) {
$_ENV = array_merge($_ENV, parse_ini_file(Path::root(self::INI), true));
// Load Composer dependencies
require_once Path::reflect(self::COMPOSER);

// Merge environment variables from user site into superglobal
if (file_exists(Path::root(self::ENV_INI))) {
$_ENV = array_merge($_ENV, parse_ini_file(Path::root(self::ENV_INI), true));
}

// Load composer dependencies from userspace if exists
if (file_exists(Path::root(self::COMPOSER_AUTLOAD))) {
require_once Path::root(self::COMPOSER_AUTLOAD);
if (file_exists(Path::root(self::COMPOSER))) {
require_once Path::root(self::COMPOSER);
}
}
}
}

/*
# Path abstractions
These methods return paths to various files and folders.
A tailing "/" is appended to each path to prevent peer dirname attacks from endpoints.
*/
class Path {
const ENDPOINTS_FOLDER = "endpoints";

// Get path to or relative path from the Reflect install directory
public static function reflect(string $crumbs = ""): string {
return dirname(__DIR__) . "/" . $crumbs;
}

// Get path to the default API class
public static function init(): string {
return self::reflect("src/api/API.php");
}

// Get path to or relative path from the user's configured root
public static function root(string $crumbs = ""): string {
return ENV::get("endpoints") . (substr($crumbs, 0, 1) === "/" ? "" : "/") . $crumbs;
return ENV::get(ENV::ENDPOINTS) . (substr($crumbs, 0, 1) === "/" ? "" : "/") . $crumbs;
}
}

Expand Down
17 changes: 9 additions & 8 deletions src/api/builtin/Call.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

namespace Reflect;

use \Reflect\ENV;
use \Reflect\Path;
use \Reflect\Response;
use \Reflect\Request\Router;
use \Reflect\Request\Method;
use \Reflect\Request\Connection;
use \Reflect\Helpers\GlobalSnapshot;
use Reflect\ENV;
use Reflect\Path;
use Reflect\Method;
use Reflect\Response;
use Reflect\Request\Router;
use Reflect\Request\Connection;
use Reflect\Helpers\GlobalSnapshot;

require_once Path::reflect("src/request/Router.php");
require_once Path::reflect("src/api/builtin/Method.php");
require_once Path::reflect("src/api/builtin/Response.php");
require_once Path::reflect("src/api/helpers/GlobalSnapshot.php");

Expand Down Expand Up @@ -56,7 +57,7 @@ function Call(string $endpoint, string|Method $method = null, array $payload = n
}

// Set flag to let stdout() know that we wish to return instead of exit.
ENV::set("INTERNAL_STDOUT", true);
ENV::set(ENV::INTERNAL_STDOUT, true);

// Start "proxied" Router (internal request)
$resp = (new Router(Connection::INTERNAL))->main();
Expand Down
7 changes: 7 additions & 0 deletions src/api/builtin/Endpoint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Reflect;

interface Endpoint {
public function main();
}
2 changes: 1 addition & 1 deletion src/request/Method.php → src/api/builtin/Method.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Reflect\Request;
namespace Reflect;

// Allowed HTTP verbs
enum Method: string {
Expand Down
Loading

0 comments on commit 83c4d49

Please sign in to comment.