Initial commit of unified WebGIS projects
This commit is contained in:
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
include_once __DIR__ . '/config.php';
|
||||
|
||||
if(session_status() === PHP_SESSION_NONE) session_start();
|
||||
|
||||
function start_session_if_needed(){ if(session_status() === PHP_SESSION_NONE) session_start(); }
|
||||
|
||||
function auth_headers(){
|
||||
if(function_exists('getallheaders')){
|
||||
$headers = getallheaders();
|
||||
if(is_array($headers)) return $headers;
|
||||
}
|
||||
$headers = [];
|
||||
foreach($_SERVER as $key => $value){
|
||||
if(strpos($key, 'HTTP_') === 0){
|
||||
$name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5)))));
|
||||
$headers[$name] = $value;
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
function auth_header_value($name){
|
||||
$headers = auth_headers();
|
||||
foreach($headers as $key => $value){
|
||||
if(strtolower($key) === strtolower($name)) return $value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function auth_json_response($statusCode, $payload){
|
||||
http_response_code($statusCode);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode($payload);
|
||||
exit;
|
||||
}
|
||||
|
||||
function base64url_encode($data){ return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); }
|
||||
function base64url_decode($data){ return base64_decode(strtr($data, '-_', '+/')); }
|
||||
|
||||
function jwt_issue($claims, $ttlSeconds = 86400){
|
||||
$now = time();
|
||||
$payload = array_merge([
|
||||
'iss' => 'webgis',
|
||||
'iat' => $now,
|
||||
'exp' => $now + max(60, intval($ttlSeconds)),
|
||||
], $claims);
|
||||
$header = ['alg' => 'HS256', 'typ' => 'JWT'];
|
||||
$encodedHeader = base64url_encode(json_encode($header));
|
||||
$encodedPayload = base64url_encode(json_encode($payload));
|
||||
$signature = hash_hmac('sha256', $encodedHeader . '.' . $encodedPayload, JWT_SECRET, true);
|
||||
return $encodedHeader . '.' . $encodedPayload . '.' . base64url_encode($signature);
|
||||
}
|
||||
|
||||
function jwt_verify($token){
|
||||
if(!$token || strpos($token, '.') === false) return null;
|
||||
$parts = explode('.', $token);
|
||||
if(count($parts) !== 3) return null;
|
||||
list($h, $p, $s) = $parts;
|
||||
$header = json_decode(base64url_decode($h), true);
|
||||
$payload = json_decode(base64url_decode($p), true);
|
||||
if(!$header || !$payload) return null;
|
||||
if(!isset($header['alg']) || $header['alg'] !== 'HS256') return null;
|
||||
$expected = base64url_encode(hash_hmac('sha256', $h . '.' . $p, JWT_SECRET, true));
|
||||
if(!hash_equals($expected, $s)) return null;
|
||||
if(isset($payload['exp']) && time() > intval($payload['exp'])) return null;
|
||||
return $payload;
|
||||
}
|
||||
|
||||
function auth_basic_credentials(){
|
||||
$header = auth_header_value('Authorization');
|
||||
if(!$header && isset($_SERVER['PHP_AUTH_USER'])){
|
||||
return [$_SERVER['PHP_AUTH_USER'], isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : ''];
|
||||
}
|
||||
if(!$header || stripos($header, 'Basic ') !== 0) return [null, null];
|
||||
$decoded = base64_decode(substr($header, 6));
|
||||
if($decoded === false || strpos($decoded, ':') === false) return [null, null];
|
||||
list($user, $pass) = explode(':', $decoded, 2);
|
||||
return [$user, $pass];
|
||||
}
|
||||
|
||||
function auth_internal_cipher(){
|
||||
return 'aes-256-gcm';
|
||||
}
|
||||
|
||||
function auth_internal_key(){
|
||||
return hash('sha256', INTERNAL_AUTH_KEY, true);
|
||||
}
|
||||
|
||||
function internal_auth_issue($claims, $ttlSeconds = 300){
|
||||
$payload = array_merge([
|
||||
'iss' => 'webgis-internal',
|
||||
'iat' => time(),
|
||||
'exp' => time() + max(30, intval($ttlSeconds)),
|
||||
'nonce' => bin2hex(random_bytes(8))
|
||||
], $claims);
|
||||
$plaintext = json_encode($payload);
|
||||
$iv = random_bytes(12);
|
||||
$tag = '';
|
||||
$ciphertext = openssl_encrypt($plaintext, auth_internal_cipher(), auth_internal_key(), OPENSSL_RAW_DATA, $iv, $tag);
|
||||
if($ciphertext === false) return null;
|
||||
return base64url_encode($iv . $tag . $ciphertext);
|
||||
}
|
||||
|
||||
function internal_auth_verify($token){
|
||||
if(!$token) return null;
|
||||
$raw = base64url_decode($token);
|
||||
if($raw === false || strlen($raw) < 28) return null;
|
||||
$iv = substr($raw, 0, 12);
|
||||
$tag = substr($raw, 12, 16);
|
||||
$ciphertext = substr($raw, 28);
|
||||
$plaintext = openssl_decrypt($ciphertext, auth_internal_cipher(), auth_internal_key(), OPENSSL_RAW_DATA, $iv, $tag);
|
||||
if($plaintext === false) return null;
|
||||
$payload = json_decode($plaintext, true);
|
||||
if(!$payload) return null;
|
||||
if(isset($payload['exp']) && time() > intval($payload['exp'])) return null;
|
||||
return $payload;
|
||||
}
|
||||
|
||||
function get_auth_context(){
|
||||
start_session_if_needed();
|
||||
global $conn;
|
||||
$context = ['user' => null, 'mode' => null];
|
||||
|
||||
$internalToken = auth_header_value('X-Internal-Auth');
|
||||
if($internalToken){
|
||||
$internal = internal_auth_verify($internalToken);
|
||||
if($internal){
|
||||
$context['mode'] = 'internal';
|
||||
$context['user'] = [
|
||||
'id' => isset($internal['sub']) ? intval($internal['sub']) : 0,
|
||||
'email' => isset($internal['email']) ? $internal['email'] : 'internal',
|
||||
'name' => isset($internal['name']) ? $internal['name'] : 'Internal',
|
||||
'organization' => isset($internal['organization']) ? $internal['organization'] : 'Internal',
|
||||
'role' => isset($internal['role']) ? $internal['role'] : 'admin',
|
||||
'status' => 'active'
|
||||
];
|
||||
return $context;
|
||||
}
|
||||
}
|
||||
|
||||
$bearer = auth_header_value('Authorization');
|
||||
if($bearer && stripos($bearer, 'Bearer ') === 0){
|
||||
$payload = jwt_verify(trim(substr($bearer, 7)));
|
||||
if($payload && isset($payload['sub'])){
|
||||
$uid = intval($payload['sub']);
|
||||
if($uid > 0 && $conn){
|
||||
$st = $conn->prepare('SELECT id, email, name, organization, role, status FROM users WHERE id = ? LIMIT 1');
|
||||
if($st){
|
||||
$st->bind_param('i', $uid);
|
||||
$st->execute();
|
||||
$r = $st->get_result();
|
||||
$row = $r ? $r->fetch_assoc() : null;
|
||||
$st->close();
|
||||
if($row){
|
||||
$context['mode'] = 'jwt';
|
||||
$context['user'] = $row;
|
||||
return $context;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list($basicUser, $basicPass) = auth_basic_credentials();
|
||||
if($basicUser !== null){
|
||||
if($basicUser === BASIC_API_USER && hash_equals(BASIC_API_PASS, (string)$basicPass)){
|
||||
$context['mode'] = 'basic';
|
||||
$context['user'] = ['id' => 0, 'email' => 'api', 'name' => 'API', 'organization' => 'API', 'role' => 'admin', 'status' => 'active'];
|
||||
return $context;
|
||||
}
|
||||
if($conn){
|
||||
$st = $conn->prepare('SELECT id, email, name, organization, role, status, password_hash FROM users WHERE email = ? LIMIT 1');
|
||||
if($st){
|
||||
$st->bind_param('s', $basicUser);
|
||||
$st->execute();
|
||||
$r = $st->get_result();
|
||||
$row = $r ? $r->fetch_assoc() : null;
|
||||
$st->close();
|
||||
if($row && password_verify((string)$basicPass, $row['password_hash'])){
|
||||
unset($row['password_hash']);
|
||||
$context['mode'] = 'basic';
|
||||
$context['user'] = $row;
|
||||
return $context;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($_SESSION['jwt'])){
|
||||
$payload = jwt_verify($_SESSION['jwt']);
|
||||
if($payload && isset($payload['sub']) && $conn){
|
||||
$uid = intval($payload['sub']);
|
||||
$st = $conn->prepare('SELECT id, email, name, organization, role, status FROM users WHERE id = ? LIMIT 1');
|
||||
if($st){
|
||||
$st->bind_param('i', $uid);
|
||||
$st->execute();
|
||||
$r = $st->get_result();
|
||||
$row = $r ? $r->fetch_assoc() : null;
|
||||
$st->close();
|
||||
if($row){
|
||||
$context['mode'] = 'jwt';
|
||||
$context['user'] = $row;
|
||||
return $context;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
function get_current_user_info(){
|
||||
$context = get_auth_context();
|
||||
return $context['user'];
|
||||
}
|
||||
|
||||
function require_auth($allowedModes = ['jwt','basic','internal']){
|
||||
$context = get_auth_context();
|
||||
$user = $context['user'];
|
||||
if(!$user){ auth_json_response(401, ['success'=>false,'error'=>'unauthenticated']); }
|
||||
if(isset($user['status']) && $user['status'] !== 'active'){
|
||||
auth_json_response(403, ['success'=>false,'error'=>'account_not_active']);
|
||||
}
|
||||
if($allowedModes && $context['mode'] && !in_array($context['mode'], (array)$allowedModes, true)){
|
||||
auth_json_response(403, ['success'=>false,'error'=>'forbidden']);
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
function require_login(){
|
||||
return require_auth(['jwt','basic','internal']);
|
||||
}
|
||||
|
||||
function require_bearer_login(){
|
||||
$context = get_auth_context();
|
||||
if(!$context['user'] || $context['mode'] !== 'jwt'){
|
||||
auth_json_response(401, ['success'=>false,'error'=>'bearer_required']);
|
||||
}
|
||||
if(isset($context['user']['status']) && $context['user']['status'] !== 'active'){
|
||||
auth_json_response(403, ['success'=>false,'error'=>'account_not_active']);
|
||||
}
|
||||
return $context['user'];
|
||||
}
|
||||
|
||||
function require_role($roles){
|
||||
$user = require_login();
|
||||
if(!in_array($user['role'], (array)$roles, true)){
|
||||
auth_json_response(403, ['success'=>false,'error'=>'forbidden']);
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
function auth_current_mode(){
|
||||
$context = get_auth_context();
|
||||
return $context['mode'];
|
||||
}
|
||||
|
||||
function auth_issue_user_token($userId, $role, $extra = [], $ttlSeconds = 86400){
|
||||
return jwt_issue(array_merge(['sub' => intval($userId), 'role' => $role], $extra), $ttlSeconds);
|
||||
}
|
||||
|
||||
function auth_set_session_jwt($token){
|
||||
start_session_if_needed();
|
||||
$_SESSION['jwt'] = $token;
|
||||
}
|
||||
|
||||
function auth_clear_session(){
|
||||
start_session_if_needed();
|
||||
foreach(['jwt','user_id'] as $key){ if(isset($_SESSION[$key])) unset($_SESSION[$key]); }
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
// Load environment from project root .env if present, then read API_KEY
|
||||
function load_dotenv($path){
|
||||
if(!file_exists($path)) return;
|
||||
$lines = file($path, FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);
|
||||
foreach($lines as $line){
|
||||
if(trim($line)==='' || strpos(trim($line),'#')===0) continue;
|
||||
if(strpos($line,'=')===false) continue;
|
||||
list($k,$v) = explode('=', $line, 2);
|
||||
$k = trim($k); $v = trim($v);
|
||||
// strip optional quotes
|
||||
if((substr($v,0,1)==='"' && substr($v,-1)==='"') || (substr($v,0,1)==="'" && substr($v,-1)==="'")){
|
||||
$v = substr($v,1,-1);
|
||||
}
|
||||
putenv("$k=$v");
|
||||
$_ENV[$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
$envPath = realpath(__DIR__ . '/../../.env');
|
||||
if($envPath){ load_dotenv($envPath); }
|
||||
|
||||
$apiKey = getenv('API_KEY');
|
||||
if(!$apiKey) {
|
||||
// fallback (development) - but prefer .env or system env in production
|
||||
$apiKey = '8f3b7e6a9c4d2f1a6b8c9d0e4f7a1b2c';
|
||||
}
|
||||
define('API_KEY', $apiKey);
|
||||
|
||||
$jwtSecret = getenv('JWT_SECRET');
|
||||
if(!$jwtSecret) {
|
||||
$jwtSecret = hash('sha256', $apiKey . ':jwt');
|
||||
}
|
||||
define('JWT_SECRET', $jwtSecret);
|
||||
|
||||
$internalKey = getenv('INTERNAL_AUTH_KEY');
|
||||
if(!$internalKey) {
|
||||
$internalKey = hash('sha256', $apiKey . ':internal');
|
||||
}
|
||||
define('INTERNAL_AUTH_KEY', $internalKey);
|
||||
|
||||
$basicApiUser = getenv('BASIC_API_USER');
|
||||
if(!$basicApiUser) { $basicApiUser = 'webgis-api'; }
|
||||
define('BASIC_API_USER', $basicApiUser);
|
||||
|
||||
$basicApiPass = getenv('BASIC_API_PASS');
|
||||
if(!$basicApiPass) { $basicApiPass = hash('sha256', $apiKey . ':basic'); }
|
||||
define('BASIC_API_PASS', $basicApiPass);
|
||||
?>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
include_once __DIR__ . '/config.php';
|
||||
|
||||
$dbHost = getenv('DB_HOST') ?: 'localhost';
|
||||
$dbPort = (int)(getenv('DB_PORT') ?: 3306);
|
||||
$dbUser = getenv('DB_USER') ?: 'root';
|
||||
$dbPass = getenv('DB_PASS') ?: 'ilham';
|
||||
$dbName = getenv('DB_NAME') ?: 'poverty_db';
|
||||
|
||||
$conn = new mysqli($dbHost, $dbUser, $dbPass, $dbName, $dbPort);
|
||||
|
||||
if ($conn->connect_error) {
|
||||
die("Koneksi gagal: " . $conn->connect_error);
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user