many changes
This commit is contained in:
parent
258dd67615
commit
39bcb09a36
10 changed files with 616 additions and 182 deletions
21
README.md
21
README.md
|
@ -4,26 +4,7 @@ LDAP Forward Auth is a forward auth service (shocking) that i made to use with t
|
|||
|
||||
It allows a proxied login page to appear if a user tries to access restricted content while not logged in.
|
||||
|
||||
It uses LDAP for authentication, which can be configured in the conf/ldap/ldap.env file.
|
||||
|
||||
```
|
||||
# the following is all the ldap credentials for a ldap bind dn auth setup
|
||||
# this does not support ldap simple auth
|
||||
LDAP_URL=
|
||||
LDAP_BIND_DN=
|
||||
LDAP_BIND_PASSWORD=
|
||||
LDAP_BASE_DN=
|
||||
LDAP_FILTER="(&)"
|
||||
LDAP_UID="cn"
|
||||
|
||||
# the host that the forward auth is hosted at
|
||||
# if the user is not logged in they will be redirected here
|
||||
HTTP_HOST=auth.example.com
|
||||
|
||||
# the base domain for all websites that are being authed checked including the forward auth itself.
|
||||
# they all need to have a common root domain otherwise the X-LDAP-Auth-Key cookie cannot be set.
|
||||
COOKIE_DOMAIN=example.com
|
||||
```
|
||||
It uses LDAP for authentication, which can be configured in the `docker.env` file.
|
||||
|
||||
Once authenticated, sets the X-Webauth-User header, which can be used by applications to see who is logged in.
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
|
||||
LDAP_URL=
|
||||
LDAP_BIND_DN=
|
||||
LDAP_BIND_PASSWORD=
|
||||
LDAP_BASE_DN=
|
||||
LDAP_FILTER="(&)"
|
||||
LDAP_UID="cn"
|
||||
|
||||
HTTP_HOST=auth.example.com
|
||||
COOKIE_DOMAIN=example.com
|
|
@ -15,7 +15,7 @@ services:
|
|||
build: ./build/php
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ./conf/ldap/ldap.env
|
||||
- docker.env
|
||||
volumes:
|
||||
- ./src:/opt/website:ro
|
||||
- ./data/session:/var/lib/php/session
|
||||
|
|
37
docker.env
Normal file
37
docker.env
Normal file
|
@ -0,0 +1,37 @@
|
|||
|
||||
#
|
||||
# ldap_forwardauth config file
|
||||
#
|
||||
|
||||
# == LDAP SETTINGS ==
|
||||
# Enter the auth information for the ldap bind dn auth
|
||||
LDAP_URL="ldap://127.0.0.1"
|
||||
LDAP_BIND_DN="cn=example,ou=users,dc=example,dc=com"
|
||||
LDAP_BIND_PASSWORD="securePassword"
|
||||
LDAP_BASE_DN="ou=users,dc=example,dc=com"
|
||||
LDAP_FILTER="(&)"
|
||||
LDAP_UID="cn"
|
||||
|
||||
# == LDAP MATCHERS ==
|
||||
# Matchers allow the program to figure out what fields
|
||||
# contain what type of data
|
||||
LDAP_USERNAME_MATCHER="uid"
|
||||
LDAP_EMAIL_MATCHER="mail"
|
||||
LDAP_FIRST_NAME_MATCHER="givenname"
|
||||
LDAP_LAST_NAME_MATCHER="sn"
|
||||
|
||||
# == FORWARD AUTH HEADER NAMES ==
|
||||
# Specify the names for the forward auth headers
|
||||
# ldap_forwardauth should output
|
||||
HTTP_USERNAME_HEADER=X-Webauth-Username
|
||||
HTTP_EMAIL_HEADER=X-Webauth-Email
|
||||
HTTP_FIRST_NAME_HEADER=X-Webauth-First-Name
|
||||
HTTP_LAST_NAME_HEADER=X-Webauth-Last-Name
|
||||
|
||||
# == HOST INFORMATION ==
|
||||
# The http host is the domain and port ldap_forwardauth is hosted at
|
||||
# Cookie domain is the superset of domains that ldap_forwardauth can auth to
|
||||
# Note: HTTPH_HOST must must be the same or a subdomain to COOKIE_DOMAIN
|
||||
HTTP_HOST=auth.example.com
|
||||
COOKIE_DOMAIN=example.com
|
||||
COOKIE_NAME=X-Webauth-Token
|
|
@ -21,7 +21,7 @@ body {
|
|||
color: #fff;
|
||||
font-family: "Open Sans", Helvetica, Arial, sans-serif;
|
||||
font-weight: 100;
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
main {
|
||||
|
|
|
@ -1,85 +1,83 @@
|
|||
<?php /* Copyright (c) 2024 Freya Murphy */
|
||||
|
||||
$keys = array();
|
||||
class AuthHelper {
|
||||
|
||||
function get_cookie() {
|
||||
$cookie_name = 'X-LDAP-Auth-Key';
|
||||
if(isset($_COOKIE[$cookie_name])) {
|
||||
return $_COOKIE[$cookie_name];
|
||||
} else {
|
||||
return FALSE;
|
||||
private $session_lifetime_seconds;
|
||||
|
||||
function __construct() {
|
||||
$this->session_lifetime_seconds = 60 * 60 * 24 * 3;
|
||||
}
|
||||
}
|
||||
|
||||
function store_cookie($key) {
|
||||
$cookie_name = 'X-LDAP-Auth-Key';
|
||||
$cookie_options = array (
|
||||
'expires' => time() + 60*60*24*30,
|
||||
'path' => '/',
|
||||
'domain' => getenv("COOKIE_DOMAIN"),
|
||||
'secure' => true,
|
||||
'httponly' => true,
|
||||
'samesite' => 'None'
|
||||
);
|
||||
setcookie(
|
||||
$cookie_name,
|
||||
$key,
|
||||
$cookie_options
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Generate a random token
|
||||
* @param int $length
|
||||
*/
|
||||
private function gen_token(int $length): string {
|
||||
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
$random = '';
|
||||
|
||||
function load_key($key) {
|
||||
$file = "/tmp/$key";
|
||||
if (!file_exists($file))
|
||||
return FALSE;
|
||||
$content = explode("\n", file_get_contents($file));
|
||||
return array(
|
||||
'user' => $content[0],
|
||||
'time' => $content[1]
|
||||
);
|
||||
}
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$index = rand(0, strlen($characters) - 1);
|
||||
$random .= $characters[$index];
|
||||
}
|
||||
|
||||
function store_key($key, $user) {
|
||||
$file = "/tmp/$key";
|
||||
$now = (string)time();
|
||||
$content = "$user\n{$now}";
|
||||
file_put_contents($file, $content, LOCK_EX);
|
||||
}
|
||||
|
||||
function get_random($n)
|
||||
{
|
||||
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
$randomString = '';
|
||||
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
$index = rand(0, strlen($characters) - 1);
|
||||
$randomString .= $characters[$index];
|
||||
}
|
||||
|
||||
return $randomString;
|
||||
}
|
||||
|
||||
function key_auth() {
|
||||
$key = get_cookie();
|
||||
if ($key === FALSE) {
|
||||
return FALSE;
|
||||
return $random;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a user into the session specified by their auth key
|
||||
* @param Session $session - the session user data
|
||||
*/
|
||||
public function save_session(Session $session): void {
|
||||
$path = "/tmp/{$session->token}";
|
||||
$data = json_encode($session->to_array());
|
||||
file_put_contents($path, $data, LOCK_EX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the auth session associated with a specific key
|
||||
* @param string $token - the session $key
|
||||
*/
|
||||
private function load_session(string $token): ?Session {
|
||||
try {
|
||||
$path = "/tmp/$token";
|
||||
if (!file_exists($path)) {
|
||||
return NULL;
|
||||
}
|
||||
$content = file_get_contents($path);
|
||||
$json = json_decode($content, TRUE);
|
||||
$session = new Session();
|
||||
if ($session->from_array($json))
|
||||
return NULL;
|
||||
return $session;
|
||||
} catch (Exception $e) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new session for a user
|
||||
*/
|
||||
public function create_session(User $user): Session {
|
||||
$session = new Session();
|
||||
$session->token = $this->gen_token(128);
|
||||
$session->created = time();
|
||||
$session->user = $user;
|
||||
$session->reset_expiry();
|
||||
$this->save_session($session);
|
||||
return $session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current authed session
|
||||
*/
|
||||
public function get_session(): ?Session {
|
||||
$cookie_name = getenv("COOKIE_NAME");
|
||||
if(!isset($_COOKIE[$cookie_name])) {
|
||||
return NULL;
|
||||
}
|
||||
$token = $_COOKIE[$cookie_name];
|
||||
return $this->load_session($token);
|
||||
}
|
||||
$data = load_key($key);
|
||||
if ($data === FALSE) {
|
||||
return FALSE;
|
||||
}
|
||||
$user = $data['user'];
|
||||
$time = $data['time'];
|
||||
$now = time();
|
||||
if ($time > $now || $now - $time > 60 * 60 * 24) {
|
||||
return FALSE;
|
||||
}
|
||||
store_key($key, $user);
|
||||
return $user;
|
||||
}
|
||||
|
||||
function key_new($user) {
|
||||
$key = get_random(128);
|
||||
store_key($key, $user);
|
||||
store_cookie($key);
|
||||
}
|
||||
|
|
|
@ -1,41 +1,120 @@
|
|||
<?php /* Copyright (c) 2024 Freya Murphy */
|
||||
|
||||
function ldap_auth($auth_username, $auth_password) {
|
||||
$url = getenv("LDAP_URL");
|
||||
$bind = getenv("LDAP_BIND_DN");
|
||||
$password = getenv("LDAP_BIND_PASSWORD");
|
||||
$bound = getenv("LDAP_BASE_DN");
|
||||
$filter = getenv("LDAP_FILTER");
|
||||
$uid = getenv("LDAP_UID");
|
||||
class LDAPHelper {
|
||||
|
||||
$conn = @ldap_connect($url);
|
||||
if (!$conn) {
|
||||
return NULL;
|
||||
}
|
||||
ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
private ?\LDAP\Connection $conn;
|
||||
private array $env;
|
||||
private array $matchers;
|
||||
|
||||
$bind_conn = @ldap_bind($conn, $bind, $password);
|
||||
if (!$bind_conn) {
|
||||
return NULL;
|
||||
private ?string $bound;
|
||||
|
||||
function __construct() {
|
||||
$this->env = array(
|
||||
# ldap host
|
||||
'url' => getenv("LDAP_URL"),
|
||||
# ldap credentials
|
||||
'bind' => getenv("LDAP_BIND_DN"),
|
||||
'password' => getenv("LDAP_BIND_PASSWORD"),
|
||||
# ldap search
|
||||
'base' => getenv("LDAP_BASE_DN"),
|
||||
'filter' => getenv("LDAP_FILTER"),
|
||||
'uid' => getenv("LDAP_UID"),
|
||||
);
|
||||
|
||||
$this->matchers = array(
|
||||
'username' => getenv("LDAP_USERNAME_MATCHER"),
|
||||
'email' => getenv("LDAP_EMAIL_MATCHER"),
|
||||
'first_name' => getenv("LDAP_FIRST_NAME_MATCHER"),
|
||||
'last_name' => getenv("LDAP_LAST_NAME_MATCHER"),
|
||||
);
|
||||
|
||||
$this->bound = NULL;
|
||||
$this->conn = NULL;
|
||||
}
|
||||
|
||||
$search = @ldap_search($conn, $bound, $filter);
|
||||
|
||||
$info = @ldap_get_entries($conn, $search);
|
||||
$user = NULL;
|
||||
for ($i=0; $i<$info['count']; $i++) {
|
||||
$user = $info[$i];
|
||||
if (!array_key_exists($uid, $user))
|
||||
continue;
|
||||
if ($user[$uid][0] == $auth_username)
|
||||
break;
|
||||
private function connect(): int {
|
||||
if (($this->conn = @ldap_connect($this->env['url'])) == FALSE) {
|
||||
$this->conn = NULL;
|
||||
return 1;
|
||||
}
|
||||
@ldap_set_option($this->conn, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($user == NULL) {
|
||||
return FALSE;
|
||||
private function rebind(): int {
|
||||
if ($this->bound != $this->env['bind']) {
|
||||
return $this->bind(
|
||||
$this->env['bind'],
|
||||
$this->env['password']);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function bind(
|
||||
string $dn,
|
||||
#[\SensitiveParameter] string $password
|
||||
): int {
|
||||
if ($this->conn == NULL && $this->connect()) {
|
||||
return 1;
|
||||
}
|
||||
if (@ldap_bind($this->conn, $dn, $password) == FALSE) {
|
||||
return 1;
|
||||
}
|
||||
$this->bound = $dn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int,mixed> $user
|
||||
*/
|
||||
private function find_entry(array $user, string $field): mixed {
|
||||
if (!isset($user[$field]))
|
||||
return NULL;
|
||||
$data = $user[$field];
|
||||
if (is_array($data))
|
||||
$data = $data[0];
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function search(
|
||||
string ...$usernames
|
||||
): ?array {
|
||||
if ($this->rebind())
|
||||
return NULL;
|
||||
|
||||
$search = @ldap_search(
|
||||
$this->conn,
|
||||
$this->env['base'],
|
||||
$this->env['filter']
|
||||
);
|
||||
if ($search == FALSE)
|
||||
return NULL;
|
||||
|
||||
$info = @ldap_get_entries($this->conn, $search);
|
||||
|
||||
$users = array();
|
||||
|
||||
for ($i=0; $i<$info['count']; $i++) {
|
||||
$user_arr = $info[$i];
|
||||
$user_data = array (
|
||||
'dn' => $user_arr['dn'],
|
||||
'username' => $this->find_entry($user_arr, $this->matchers['username']),
|
||||
'email' => $this->find_entry($user_arr, $this->matchers['email']),
|
||||
'first_name' => $this->find_entry($user_arr, $this->matchers['first_name']),
|
||||
'last_name' => $this->find_entry($user_arr, $this->matchers['last_name'])
|
||||
);
|
||||
$user = new User();
|
||||
if ($user->from_array($user_data)) {
|
||||
continue;
|
||||
}
|
||||
if (count($usernames) && !in_array($user->username, $usernames)) {
|
||||
continue;
|
||||
}
|
||||
$users[] = $user;
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
$succ = @ldap_bind($conn, $user['dn'], $auth_password);
|
||||
return !!$succ;
|
||||
}
|
||||
|
||||
|
|
236
src/web/helpers/schema.php
Normal file
236
src/web/helpers/schema.php
Normal file
|
@ -0,0 +1,236 @@
|
|||
<?php /* Copyright (c) 2024 Freya Murphy */
|
||||
|
||||
class User {
|
||||
|
||||
public ?string $dn;
|
||||
public ?string $username;
|
||||
public ?string $email;
|
||||
public ?string $first_name;
|
||||
public ?string $last_name;
|
||||
|
||||
function __construct() {}
|
||||
|
||||
/**
|
||||
* Validates all required fields are set
|
||||
*/
|
||||
private function validate(): int {
|
||||
return (
|
||||
$this->dn == NULL ||
|
||||
$this->username == NULL ||
|
||||
$this->email == NULL
|
||||
) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Data from the array to self
|
||||
* @param array $data - the data to load
|
||||
* @return int 0 on success, 1 on error
|
||||
*/
|
||||
public function from_array(array $data): int {
|
||||
$this->dn = NULL;
|
||||
$this->username = NULL;
|
||||
$this->email = NULL;
|
||||
$this->first_name = NULL;
|
||||
$this->last_name = NULL;
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if ($value == NULL)
|
||||
continue;
|
||||
$type = gettype($value);
|
||||
switch ($key) {
|
||||
case 'dn': {
|
||||
if ($type != 'string')
|
||||
return 1;
|
||||
$this->dn = $value;
|
||||
} break;
|
||||
case 'username': {
|
||||
if ($type != 'string')
|
||||
return 1;
|
||||
$this->username = $value;
|
||||
} break;
|
||||
case 'email': {
|
||||
if ($type != 'string')
|
||||
return 1;
|
||||
$this->email = $value;
|
||||
} break;
|
||||
case 'first_name': {
|
||||
if ($type != 'string')
|
||||
return 1;
|
||||
$this->first_name = $value;
|
||||
} break;
|
||||
case 'last_name': {
|
||||
if ($type != 'string')
|
||||
return 1;
|
||||
$this->last_name = $value;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the user into an array
|
||||
* @return ?array<string,string>
|
||||
*/
|
||||
public function to_array(): ?array {
|
||||
if ($this->validate())
|
||||
return NULL;
|
||||
$data = array(
|
||||
'dn' => $this->dn,
|
||||
'username' => $this->username,
|
||||
'email' => $this->email
|
||||
);
|
||||
if ($this->first_name)
|
||||
$data['first_name'] = $this->first_name;
|
||||
if ($this->last_name)
|
||||
$data['last_name'] = $this->last_name;
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the HTTP headers
|
||||
*/
|
||||
public function write_headers(): int {
|
||||
if ($this->validate())
|
||||
return 1;
|
||||
|
||||
$header_username = getenv("HTTP_USERNAME_HEADER");
|
||||
$header_email = getenv("HTTP_EMAIL_HEADER");
|
||||
$header_first = getenv("HTTP_FIRST_NAME_HEADER");
|
||||
$header_last = getenv("HTTP_LAST_NAME_HEADER");
|
||||
|
||||
header("{$header_username}: {$this->username}");
|
||||
header("{$header_email}: {$this->email}");
|
||||
if ($this->first_name)
|
||||
header("{$header_first}: {$this->first_name}");
|
||||
if ($this->last_name)
|
||||
header("{$header_last}: {$this->last_name}");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Session {
|
||||
|
||||
public ?User $user;
|
||||
public ?int $created;
|
||||
public ?int $expires;
|
||||
public ?string $token;
|
||||
|
||||
private int $session_lifetime_seconds;
|
||||
|
||||
function __construct() {
|
||||
$this->session_lifetime_seconds = 60 * 60 * 24 * 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates all required fields are set
|
||||
*/
|
||||
private function validate(): int {
|
||||
if (
|
||||
$this->user == NULL ||
|
||||
$this->created == NULL ||
|
||||
$this->expires == NULL ||
|
||||
$this->token == NULL
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
if ($this->expires < time())
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Data from the array to self
|
||||
* @param array $data - the data to load
|
||||
* @return int 0 on success, 1 on error
|
||||
*/
|
||||
public function from_array(array $data): int {
|
||||
$this->user = NULL;
|
||||
$this->created = NULL;
|
||||
$this->expires = NULL;
|
||||
$this->token = NULL;
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if ($value == NULL)
|
||||
continue;
|
||||
$type = gettype($value);
|
||||
switch ($key) {
|
||||
case 'user': {
|
||||
$this->user = new User();
|
||||
if ($this->user->from_array($value))
|
||||
return 1;
|
||||
} break;
|
||||
case 'created': {
|
||||
if ($type != 'integer')
|
||||
return 1;
|
||||
$this->created = $value;
|
||||
} break;
|
||||
case 'expires': {
|
||||
if ($type != 'integer')
|
||||
return 1;
|
||||
$this->expires = $value;
|
||||
} break;
|
||||
case 'token': {
|
||||
if ($type != 'string')
|
||||
return 1;
|
||||
$this->token = $value;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renew the expiry clock
|
||||
*/
|
||||
public function reset_expiry(): void {
|
||||
$this->expires = time() + $this->session_lifetime_seconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the session into an array
|
||||
* @return ?array<string,mixed>
|
||||
*/
|
||||
public function to_array(): ?array {
|
||||
if ($this->validate())
|
||||
return NULL;
|
||||
return array(
|
||||
'user' => $this->user->to_array(),
|
||||
'created' => $this->created,
|
||||
'expires' => $this->expires,
|
||||
'token' => $this->token
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the HTTP headers
|
||||
*/
|
||||
public function write_headers(): int {
|
||||
if ($this->validate())
|
||||
return 1;
|
||||
if ($this->user->write_headers())
|
||||
return 1;
|
||||
|
||||
$cookie_name = getenv("COOKIE_NAME");
|
||||
$cookie_options = array (
|
||||
'expires' => $this->expires,
|
||||
'path' => '/',
|
||||
'domain' => getenv("COOKIE_DOMAIN"),
|
||||
'secure' => true,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax'
|
||||
);
|
||||
setcookie(
|
||||
$cookie_name,
|
||||
$this->token,
|
||||
$cookie_options
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -6,52 +6,11 @@ $webroot = dirname(__FILE__);
|
|||
$publicroot = realpath(dirname(__FILE__) . '/../public');
|
||||
|
||||
// load stuff
|
||||
require($webroot . '/helpers/schema.php');
|
||||
require($webroot . '/helpers/ldap.php');
|
||||
require($webroot . '/helpers/auth.php');
|
||||
require($webroot . '/router.php');
|
||||
|
||||
// start session
|
||||
function page($file, $data = array()) {
|
||||
extract($data);
|
||||
$webroot = $GLOBALS['webroot'];
|
||||
require($webroot . '/views/header.php');
|
||||
require($webroot . "/views/$file.php");
|
||||
require($webroot . '/views/footer.php');
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
parse_str(file_get_contents('php://input'), $post);
|
||||
$res = ldap_auth($post['username'], $post['password']);
|
||||
$msg = '';
|
||||
$title = '';
|
||||
if ($res) {
|
||||
$msg = 'Authenticated. You can now go back to your content';
|
||||
$title = 'Success';
|
||||
key_new($post['username']);
|
||||
} else {
|
||||
$msg = 'Invalid Credentials';
|
||||
$title = 'Error';
|
||||
}
|
||||
page('message', array(
|
||||
'title' => $title,
|
||||
'msg' => $msg
|
||||
));
|
||||
} else {
|
||||
if (($user = key_auth())) {
|
||||
http_response_code(200);
|
||||
header("X-Webauth-User: $user");
|
||||
die();
|
||||
}
|
||||
|
||||
$host = $_SERVER['HTTP_HOST'];
|
||||
$env = getenv("HTTP_HOST");
|
||||
if ($_SERVER['REQUEST_URI'] !== '/login') {
|
||||
// we are being forwarded authed
|
||||
// redirect
|
||||
http_response_code(303);
|
||||
header("Location: http://$env/login");
|
||||
} else {
|
||||
page('login', array(
|
||||
'title' => 'Login'
|
||||
));
|
||||
}
|
||||
}
|
||||
// do the
|
||||
$router = new Router();
|
||||
$router->handle();
|
||||
|
|
154
src/web/router.php
Normal file
154
src/web/router.php
Normal file
|
@ -0,0 +1,154 @@
|
|||
<?php /* Copyright (c) 2024 Freya Murphy */
|
||||
|
||||
class Router {
|
||||
|
||||
private $ldap;
|
||||
private $auth;
|
||||
|
||||
private $domain;
|
||||
|
||||
function __construct() {
|
||||
$this->ldap = new LDAPHelper();
|
||||
$this->auth = new AuthHelper();
|
||||
|
||||
$this->domain = getenv("HTTP_HOST");
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a page to the user
|
||||
* @param string $file
|
||||
* @param array<string,mixed> $data
|
||||
*/
|
||||
private function send_page(
|
||||
string $file,
|
||||
array $data = array()
|
||||
): void {
|
||||
extract($data);
|
||||
$webroot = $GLOBALS['webroot'];
|
||||
require($webroot . '/views/header.php');
|
||||
require($webroot . "/views/$file.php");
|
||||
require($webroot . '/views/footer.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a message to the user (message page)
|
||||
* @param string $title
|
||||
* @param string $msg
|
||||
* @param int $code
|
||||
*/
|
||||
private function send_message(
|
||||
string $title,
|
||||
string $msg
|
||||
): void {
|
||||
$this->send_page('message', array(
|
||||
'title' => $title,
|
||||
'msg' => $msg
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTTP request information
|
||||
*/
|
||||
private function get_req(): array {
|
||||
return array(
|
||||
'path' => $_SERVER['REQUEST_URI'],
|
||||
'method' => $_SERVER['REQUEST_METHOD'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string> $fields
|
||||
*/
|
||||
private function get_post_info(
|
||||
string ...$fields
|
||||
): ?array {
|
||||
$values = array();
|
||||
|
||||
try {
|
||||
$temp = NULL;
|
||||
parse_str(file_get_contents('php://input'), $temp);
|
||||
foreach ($temp as $key => $value) {
|
||||
$_POST[$key] = $value;
|
||||
}
|
||||
} catch (Exception $_e) {}
|
||||
|
||||
foreach ($fields as $key) {
|
||||
if (!isset($_POST[$key]))
|
||||
return NULL;
|
||||
$values[$key] = $_POST[$key];
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
private function handle_login(): void {
|
||||
$info = $this->get_post_info('username', 'password');
|
||||
if ($info == NULL) {
|
||||
http_response_code(400);
|
||||
$this->send_message('Bad Requet', 'Credentials were not supplied');
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $this->ldap->search($info['username']);
|
||||
if ($user == NULL || !count($user)) {
|
||||
http_response_code(400);
|
||||
$this->send_message('Bad Requst', 'User does not exist');
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $user[0];
|
||||
|
||||
if ($this->ldap->bind(
|
||||
$user->dn,
|
||||
$info['password']
|
||||
)) {
|
||||
http_response_code(400);
|
||||
$this->send_message('Bad Requst', 'Invalid Credentials');
|
||||
return;
|
||||
}
|
||||
|
||||
$session = $this->auth->create_session($user);
|
||||
|
||||
http_response_code(200);
|
||||
$session->write_headers();
|
||||
$this->send_message('Success', 'Authenticated. You can now go back to your content');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the HTTP request
|
||||
* @param array<string,string> $req
|
||||
*/
|
||||
private function handle_req(array $req): void {
|
||||
if ($req['method'] == 'POST') {
|
||||
$this->handle_login();
|
||||
return;
|
||||
}
|
||||
$session = $this->auth->get_session();
|
||||
if ($session == NULL) {
|
||||
// user is NOT authenticated
|
||||
if ($req['path'] == '/login') {
|
||||
// user is requesting login page
|
||||
http_response_code(200);
|
||||
$this->send_page('login', array(
|
||||
'title' => 'Login'
|
||||
));
|
||||
} else {
|
||||
// user is trying to forward auth
|
||||
// redirect them to login
|
||||
http_response_code(303);
|
||||
header("Location: http://{$this->domain}/login");
|
||||
}
|
||||
} else {
|
||||
// user is authenticated
|
||||
$session->reset_expiry();
|
||||
$session->write_headers();
|
||||
$this->auth->save_session($session);
|
||||
}
|
||||
}
|
||||
|
||||
public function handle(): void {
|
||||
$req = $this->get_req();
|
||||
$this->handle_req($req);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue