From d6f4610bd9ee272ceb850e8916ccb3bb8896e4a6 Mon Sep 17 00:00:00 2001 From: Joshua P Panter Date: Tue, 23 Jan 2018 10:39:07 -0500 Subject: [PATCH] Massive update. fully functioning and (as far as I can tell) specification compliant server server accepts json objects or regular GET requests (important as both handle multiple 'rel' key's in one request) server storage schema moved to fully file based, scrapped sqlite code for the time being. server-manager added for the creation and manipulation of resources server-manager accepts json onbjects for complex operations or regular POST requests server manager is authoritative and secure server-manager can add and remove hosts (which must be configured before adding resource accounts) and resources server-manager can manipulate various data in the resource accounts (currently alias data, subject and properties are WIP) server-manager can alter account password rebranded from PHP-Webfinger to PHP Easy Webfinger Server (PEWS: Pew-Pew) created logo and favicon (intended for a web-based manager frontend) ships with apache config file for the management of multiple domains on a single server --- .well-known/webfinger/index.php | 609 ++++++++++++++++++ .../webfinger/store/example.com/john.json | 28 + assets/favicon.ico | Bin 0 -> 4286 bytes assets/logo.png | Bin 0 -> 24255 bytes assets/pews.conf | 16 + 5 files changed, 653 insertions(+) create mode 100755 .well-known/webfinger/index.php create mode 100755 .well-known/webfinger/store/example.com/john.json create mode 100644 assets/favicon.ico create mode 100644 assets/logo.png create mode 100644 assets/pews.conf diff --git a/.well-known/webfinger/index.php b/.well-known/webfinger/index.php new file mode 100755 index 0000000..32726cf --- /dev/null +++ b/.well-known/webfinger/index.php @@ -0,0 +1,609 @@ + * + * * + ------------------------------------------------------------ +*/ +/* + CONFIG +*/ +// Set an alternate location for the data store. note: no trailing slash +define( 'PEWS_DATA_STORE', 'store' ); +// force query and server hosts to match, maybe +define( 'PEWS_DOMAIN_STRICT', false ); +// allow a user to edit their own data? +define( 'PEWS_USER_SELF_EDIT', true ); +// Begin PEWS server //------------------ DO NOT EDIT ANYTHING BELOW THIS LINE (Unless you REALLY mean it!) ------------------// +$req = $_SERVER['REQUEST_METHOD']; +if ($req === 'GET') { + // are we receiving a JSON object? + function isValidJSON($str) { + json_decode($str); + return json_last_error() == JSON_ERROR_NONE; + } + $json_params = file_get_contents("php://input"); + if (strlen($json_params) > 0 && isValidJSON($json_params)) { + $rels = false; + $json_object = true; + $json_params = str_replace("{", "", $json_params); + $json_params = str_replace("}", "", $json_params); + $json_params = explode(',', $json_params); + foreach($json_params as $jp) { + $jp = str_replace('"', '', $jp); + $jp = explode(':', $jp, 2); + if($jp[0]=='resource') $_GET['resource']=$jp[1]; + if($jp[0]=='rel') $rels[]=$jp[1]; + } + } else { $json_object = false; } + // JSON object or not, ready to process data + if( isset($_GET['resource'])) { + $subject = explode(':', $_GET['resource']); + if($subject[0] === 'acct') { + if(strpos($subject[1], '@')) { + $acct = explode('@', $subject[1]); + $user = preg_replace('/^((\.*)(\/*))*/', '', $acct[0]); + $host = preg_replace('/^((\.*)(\/*))*/', '', $acct[1]); + if(PEWS_DOMAIN_STRICT && $host !== $_SERVER['HTTP_HOST']) { + http_response_code(400); + header("Content-Type: application/json"); + print json_encode(array( + 'statusCode' => 400, + 'message' => "Query and server hosts do not match." + ), JSON_UNESCAPED_SLASHES); + die(); + } + } else { + $user = preg_replace('/^((\.*)(\/*))*/', '', $subject[1]); + $host = $_SERVER['HTTP_HOST']; + } + $acct_file = PEWS_DATA_STORE."/".$host."/".$user.".json"; + // is there an account on file? + if (file_exists($acct_file)) { + // retrieve resource file and remove PEWS info + $data = json_decode(file_get_contents($acct_file), true); + if(isset($data['PEWS'])) unset($data['PEWS']); + // check for rel request + if($json_object == false) { + if( isset($_GET['rel'])) { + // disect string for multiple 'rel' values + $query = explode('&', $_SERVER['QUERY_STRING']); + $array = array(); + foreach( $query as $param ) { + list($key, $value) = explode('=', $param, 2); + if($key == 'rel') { + $array[urldecode($key)][] = urldecode($value); + $rels = $array['rel']; + } + } + } + } + if(isset($rels) && $rels !== false) { + // check resource data against rel request + $links = $data['links']; + $result = null; + foreach($rels as $rel) { + foreach($links as $link) if($link['rel'] == $rel) $result[] = $link; + } + $data['links'] = ($result == null) ? $data['links'] : $result; + $return = $data; + + + } else { + $return = $data; + } + // set return headers, response code, and return data + header('Access-Control-Allow-Origin: *'); + http_response_code(200); + } else { + http_response_code(404); + $return['statusCode'] = 404; + $return['message'] = 'Account ['.$subject[1].'] not found.'; + } + } else { + http_response_code(400); + $return['statusCode'] = 400; + $return['message'] = 'Malformed query: ['.$subject[0].'] not recognized.'; + } + } else { + http_response_code(400); + $return['statusCode'] = 400; + $return['message'] = "Missing 'resource' parameter, please check your query."; + } + header("Content-Type: application/json"); + print json_encode($return, JSON_UNESCAPED_SLASHES); + die(); +// Begin PEWS manager +} elseif ($req === 'POST') { + // are we receiving a JSON object? + function isValidJSON($str) { + json_decode($str); + return json_last_error() == JSON_ERROR_NONE; + } + $json_params = file_get_contents("php://input"); + if (strlen($json_params) > 0 && isValidJSON($json_params)) + $_POST = json_decode($json_params, true); + // JSON object or not, ready to process data + if(isset($_POST['pass'])) { + $pass = $_POST['pass']; + if (isset($_POST['auth'])) { + $user = $_POST['auth']; + $auth = pews_auth($user, $pass, true); + if(!$auth['is']) { + http_response_code(401); + $return['info'] = $auth['info']; + } else { + if($auth['class'] == 'admin') $return = pews_manager(true, null); + else $return = pews_manager(false, $pass); + } + } else $return = pews_manager(false, $pass); + } else { + http_response_code(403); + $return['info'] = 'forbidden'; + } + header("Content-Type: application/json"); + print json_encode($return, JSON_UNESCAPED_SLASHES); + die(); +} else { + header("Content-Type: application/json"); + http_response_code(405); + print json_encode(array( + 'statusCode' => 405, + 'info' => 'method not allowed' + ), JSON_UNESCAPED_SLASHES); + die(); +} + +function pews_auth( $acct, $key, $admin ) { + if(strpos($acct, '@')) { + $acct = explode('@', $acct); + $user = preg_replace('/^((\.*)(\/*))*/', '', $acct[0]); + $host = preg_replace('/^((\.*)(\/*))*/', '', $acct[1]); + if(!$admin && $host !== $_SERVER['HTTP_HOST']) { + $return['is'] = false; + $return['info'] = 'This is not your account'; + return $return; + } + } else { + $user = preg_replace('/^((\.*)(\/*))*/', '', $acct); + $host = $_SERVER['HTTP_HOST']; + } + $acct_file = PEWS_DATA_STORE."/".$host."/".$user.".json"; + // is there an account on file? + if(file_exists($acct_file)) { + $data = json_decode(file_get_contents($acct_file), true); + $userData = $data['PEWS']; + $class = $userData['class']; + $lock = $userData['pass']; + if(strpos($lock, 'pews-hashed') === false ) { + $hashit = pews_hash_pass($acct_file); + if(!$hashit['is'] ) die($hashit['info']); + if($lock == $key ) { + $return['is'] = true; + $return['info'] = $hashit['info']; + $return['class'] = $class; + } else { + $return['is'] = false; + $return['info'] = 'bad password'; + } + } else { + $hashLock = explode(':', $lock); + $hashLock = $hashLock[1]; + if(password_verify($key, $hashLock)) { + $return['is'] = true; + $return['class'] = $class; + } else { + $return['is'] = false; + $return['info'] = 'bad password'; + } + } + } else { + $return['is'] = false; + $return['info'] = 'bad user name'; + } + return $return; +} +function pews_hash_pass($acct_file) { + $data = json_decode(file_get_contents($acct_file), true); + if($data == false) { + $return['is'] = false; + $return['info'] = 'Could not read auth file'; + } else { + $userData = $data['PEWS']; + $class = $userData['class']; + $lock = $userData['pass']; + $to_hash = 0; + + if(strpos($lock, 'pews-hashed') === false) { + $to_hash++; + $hash = password_hash( $lock, PASSWORD_DEFAULT); + $data['PEWS'] = array('class' => $class, 'pass' => 'pews-hashed:'.$hash); + } + + if($to_hash == 0) { + $return['is'] = true; + $return['info'] = 'Nothing to hash'; + } else { + $data = json_encode($data, JSON_UNESCAPED_SLASHES); + $success = file_put_contents( $acct_file, $data ); + if($success === false) { + $return['is'] = false; + $return['info'] = 'Could not write to auth file'; + } else { + $return['is'] = true; + $return['info'] = 'password hashed'; + } + } + } + return $return; +} +function pews_manager( $auth, $password ) { + // add a new host to the server TODO url validations, etc + if(isset($_POST['addHost'])) { + if($auth) { + $host = $_POST['addHost']; + $new = PEWS_DATA_STORE . '/' . $host; + if (!file_exists($new)){ + $make = mkdir($new); + if(!$make) { + http_response_code(500); + $return['statusCode'] = 500; + $return['message'] = 'host not created'; + } else { + chmod( $new, 0755 ); + http_response_code(201); + $return['statusCode'] = 201; + $return['message'] = 'host: '.$host.' successfully added'; + } + } else { + http_response_code(200); + $return['statusCode'] = 200; + $return['message'] = 'host already present'; + } + } else { + http_response_code(403); + $return['info'] = 'forbidden'; + } + return $return; + // delete a host AND all resources + } elseif(isset($_POST['delHost'])) { + if($auth) { + $host = $_POST['delHost']; + $old = PEWS_DATA_STORE . '/' . $host; + if (file_exists($old)) { + $files = glob($old.'/*'); + foreach($files as $file) { + if(is_file($file)) + unlink($file); + } + $destroy = rmdir($old); + if(!$destroy) { + http_response_code(500); + $return['statusCode'] = 500; + $return['message'] = 'host not destroyed, but the accounts probably were.'; + } else { + http_response_code(200); + $return['statusCode'] = 200; + $return['message'] = 'host: '.$host.' successfully removed'; + } + } else { + http_response_code(200); + $return['statusCode'] = 200; + $return['message'] = 'host already absent'; + } + } else { + http_response_code(403); + $return['info'] = 'forbidden'; + } + return $return; + // Add a new resource account! + } elseif(isset($_POST['addResource'])) { + if($auth) { + $acct = $_POST['addResource']; + if(strpos($acct, '@')) { + $part = explode('@', $acct); + $user = preg_replace('/^((\.*)(\/*))*/', '', $part[0]); + $host = preg_replace('/^((\.*)(\/*))*/', '', $part[1]); + } else { + $user = preg_replace('/^((\.*)(\/*))*/', '', $acct); + $host = $_SERVER['HTTP_HOST']; + $acct = $user.'@'.$host; + } + $newHost = PEWS_DATA_STORE . '/' . $host; + if (!file_exists($newHost)){ + http_response_code(404); + $response['statusCode'] = ['404']; + $response['message'] = ['Host '.$host.' is not present on this system. Create that first, then add resource accounts to it.']; + } else { + $newUser = $newHost.'/'.$user.'.json'; + if (!file_exists($newUser)){ + $class = isset($_POST['setClass']) && ($_POST['setClass'] === 'admin' || $_POST['setClass'] === 'user') ? + $_POST['setClass'] : + 'user'; + $pass= isset($_POST['setPass']) ? 'pews-hashed:'.password_hash($_POST['setPass'], PASSWORD_DEFAULT) : 'pewpewpassword'; + $data['PEWS'] = array( 'class' => $class, 'pass' => $pass ); + $data['subject'] = 'acct:'.$acct; + if(isset($_POST['setAliases'])) { + $aliases = $_POST['setAliases']; + $data['aliases'] = is_array($aliases) ? $aliases : array($aliases); + } + if(isset($_POST['setProps'])) { + if(is_array($_POST['setProps'])) { + $data['properties'] = $_POST['setProps']; + } elseif(isset($_POST['setPropKey']) && isset($_POST['setPropVal'])) { + $data['properties'] = array($_POST['setPropKey'] => $_POST['setPropVal']); + } + } + if(isset($_POST['setLinks'])) { + if(is_array($_POST['setLinks'])) { + $data['links'] = $_POST['setLinks']; + } elseif(isset($_POST['setLinkRel']) && isset($_POST['setLinkHref'])) { + $link['rel'] = $_POST['setLinkRel']; + $link['href'] = $_POST['setLinkHref']; + $link['type'] = isset($_POST['setLinkType']) ? $_POST['setLinkType'] : null; + $link['titles'] = isset($_POST['setLinkTitles']) && is_array($_POST['setLinkTitles']) ? + $_POST['setLinkTitles'] : + isset($_POST['setLinkTitleLang']) && isset($_POST['setLinkTitle']) ? + array($_POST['setLinkTitleLang'] => $_POST['setLinkTitle']) : + null; + $link['properties'] = isset($_POST['setLinkProps']) && is_array($_POST['setLinkProps']) ? + $_POST['setLinkProps'] : + isset($_POST['setLinkPropKey']) && isset($_POST['setLinkPropVal']) ? + array($_POST['setLinkPropKey'] => $_POST['setLinkPropVal']) : + null; + foreach($link as $k => $v) { + if($v == null) unset($link[$k]); + } + + $data['links'] = $link; + } + } + // Create the resource!!! + $success = file_put_contents( $newUser, json_encode($data, JSON_UNESCAPED_SLASHES) ); + if(!$success) { + http_response_code(500); + $return['statusCode'] = 500; + $return['message'] = 'Resource not created'; + } else { + chmod( $newUser, 0755 ); + http_response_code(201); + $return['statusCode'] = 201; + $return['message'] = 'Resource: '.$acct.' successfully added'; + } + } else { + http_response_code(200); + $return['statusCode'] = 200; + $return['message'] = 'Resource already present'; + } + } + } else { + http_response_code(403); + $return['info'] = 'forbidden'; + } + return $return; + // Remove a resource/account from the server + } elseif(isset($_POST['delResource'])) { + if($auth) { + $acct = $_POST['delResource']; + if(strpos($acct, '@')) { + $part = explode('@', $acct); + $user = preg_replace('/^((\.*)(\/*))*/', '', $part[0]); + $host = preg_replace('/^((\.*)(\/*))*/', '', $part[1]); + } else { + $user = preg_replace('/^((\.*)(\/*))*/', '', $acct); + $host = $_SERVER['HTTP_HOST']; + $acct = $user.'@'.$host; + } + $acct_file = PEWS_DATA_STORE."/".$host."/".$user.".json"; + if (file_exists($acct_file)) { + $destroy = unlink($acct_file); + if(!$destroy) { + http_response_code(500); + $return['statusCode'] = 500; + $return['message'] = 'Server Error: resource not destroyed.'; + } else { + http_response_code(200); + $return['statusCode'] = 200; + $return['message'] = 'Acct: '.$acct.' successfully removed'; + } + } else { + http_response_code(200); + $return['statusCode'] = 200; + $return['message'] = 'Acct already absent'; + } + } else { + http_response_code(403); + $return['info'] = 'forbidden'; + } + return $return; + // adding an alias to a resource + } elseif(isset($_POST['addAlias'])) { + $acct = $_POST['addAlias']; + if(strpos($acct, '@')) { + $part = explode('@', $acct); + $user = preg_replace('/^((\.*)(\/*))*/', '', $part[0]); + $host = preg_replace('/^((\.*)(\/*))*/', '', $part[1]); + } else { + $user = preg_replace('/^((\.*)(\/*))*/', '', $acct); + $host = $_SERVER['HTTP_HOST']; + $acct = $user.'@'.$host; + } + switch ($auth) { + case false: + $reauth = pews_auth( $acct, $password, false ); + $auth = $reauth['is']; + case true: + if(isset($_POST['newAlias'])) { + $newAlias = $_POST['newAlias']; + $acct_file = PEWS_DATA_STORE . '/' . $host .'/'. $user . '.json'; + if (file_exists($acct_file)) { + $data = json_decode(file_get_contents($acct_file), true); + $aliases = isset($data['aliases']) ? $data['aliases'] : array(); + $aliases[] = $newAlias; + $data['aliases'] = $aliases; + $data = json_encode($data, JSON_UNESCAPED_SLASHES); + $success = file_put_contents( $acct_file, $data ); + if($success === false) { + $return['is'] = false; + $return['info'] = 'Could not write to resource file'; + } else { + $return['is'] = true; + $return['info'] = 'Alias: '.$newAlias.' added to '.$acct; + } + } else { + http_response_code(404); + $return['statusCode'] = 404; + $return['message'] = 'Account ['.$acct.'] not found.'; + } + } else { + http_response_code(400); + $return['statusCode'] = 400; + $return['message'] = "Missing newAlias, please check your query,"; + } + break; + default: + http_response_code(401); + $return['statusCode'] = 401; + $return['message'] = "You can add an alias if you know your credentials"; + $return['info'] = $reauth['info']; + } + // remove an alias from a resource + } elseif(isset($_POST['delAlias'])) { + $acct = $_POST['delAlias']; + if(strpos($acct, '@')) { + $part = explode('@', $acct); + $user = preg_replace('/^((\.*)(\/*))*/', '', $part[0]); + $host = preg_replace('/^((\.*)(\/*))*/', '', $part[1]); + } else { + $user = preg_replace('/^((\.*)(\/*))*/', '', $acct); + $host = $_SERVER['HTTP_HOST']; + $acct = $user.'@'.$host; + } + switch ($auth) { + case false: + $reauth = pews_auth( $acct, $password, false ); + $auth = $reauth['is']; + case true: + if(isset($_POST['oldAlias'])) { + $oldAlias = $_POST['oldAlias']; + $acct_file = PEWS_DATA_STORE . '/' . $host .'/'. $user . '.json'; + if (file_exists($acct_file)) { + $data = json_decode(file_get_contents($acct_file), true); + $aliases = isset($data['aliases']) ? $data['aliases'] : null; + if($aliases !== null && in_array( $oldAlias, $aliases ) ) { + $oldAliasArray[] = $oldAlias; + $newAliasesArray = array_diff( $aliases , $oldAliasArray); + if(empty($newAliasesArray)) { + unset ($data['aliases']); + } else { + $data['aliases'] = $newAliasesArray; + } + $data = json_encode($data, JSON_UNESCAPED_SLASHES); + $success = file_put_contents( $acct_file, $data ); + if($success === false) { + http_response_code(500); + $return['is'] = false; + $return['info'] = 'Could not write to resource file'; + } else { + $return['is'] = true; + $return['info'] = 'Alias: '.$oldAlias.' removed '.$acct; + } + } else { + http_response_code(100); + $return['is'] = false; + $return['info'] = 'Nothing to do: Alias '.$oldAlias.' not found.'; + } + } else { + http_response_code(404); + $return['statusCode'] = 404; + $return['message'] = 'Account ['.$acct.'] not found.'; + } + } else { + http_response_code(400); + $return['statusCode'] = 400; + $return['message'] = "Missing oldAlias, please check your query,"; + } + break; + default: + http_response_code(401); + $return['statusCode'] = 401; + $return['message'] = "You can remove an alias if you know your credentials"; + $return['info'] = $reauth['info']; + } + } elseif(isset($_POST['addProp'])) { + // Do Something + } elseif(isset($_POST['editProp'])) { + // Do Something + } elseif(isset($_POST['delProp'])) { + // Do Something + } elseif(isset($_POST['addLink'])) { + // Do Something + } elseif(isset($_POST['editLink'])) { + // Do Something + } elseif(isset($_POST['delLink'])) { + // Update a Password + } elseif(isset($_POST['updatePass'])) { + $acct = $_POST['updatePass']; + if(strpos($acct, '@')) { + $part = explode('@', $acct); + $user = preg_replace('/^((\.*)(\/*))*/', '', $part[0]); + $host = preg_replace('/^((\.*)(\/*))*/', '', $part[1]); + } else { + $user = preg_replace('/^((\.*)(\/*))*/', '', $acct); + $host = $_SERVER['HTTP_HOST']; + $acct = $user.'@'.$host; + } + switch ($auth) { + case false: + $reauth = pews_auth( $acct, $password, false ); + $auth = $reauth['is']; + case true: + if(isset($_POST['newPass'])) { + $newPass = $_POST['newPass']; + $acct_file = PEWS_DATA_STORE . '/' . $host .'/'. $user . '.json'; + if (file_exists($acct_file)) { + $data = json_decode(file_get_contents($acct_file), true); + $userData = $data['PEWS']; + $class = $userData['class']; + $hash = password_hash( $newPass, PASSWORD_DEFAULT); + $data['PEWS'] = array('class' => $class, 'pass' => 'pews-hashed:'.$hash); + $data = json_encode($data, JSON_UNESCAPED_SLASHES); + $success = file_put_contents( $acct_file, $data ); + if($success === false) { + $return['is'] = false; + $return['info'] = 'Could not write to auth file'; + } else { + $return['is'] = true; + $return['info'] = 'password updated'; + } + } else { + http_response_code(404); + $return['statusCode'] = 404; + $return['message'] = 'Account ['.$acct.'] not found.'; + } + } else { + http_response_code(400); + $return['statusCode'] = 400; + $return['message'] = "Missing newPass, please check your query,"; + } + break; + default: + http_response_code(401); + $return['statusCode'] = 401; + $return['message'] = "You can change your own password if you know your credentials"; + $return['info'] = $reauth['info']; + } + } else { + http_response_code(400); + $return['statusCode'] = 400; + $return['message'] = "Missing parameter, please check your query,"; + } + return $return; +} +?> diff --git a/.well-known/webfinger/store/example.com/john.json b/.well-known/webfinger/store/example.com/john.json new file mode 100755 index 0000000..9413e14 --- /dev/null +++ b/.well-known/webfinger/store/example.com/john.json @@ -0,0 +1,28 @@ +{ + "subject" : "acct:john@example.com", + "properties" : { "http://packetizer.com/ns/name" : "John Smith" }, + "links" : + [ + { + "rel" : "http://webfinger.net/rel/avatar", + "type" : "image/jpeg", + "href" : "http://example.com/images/face.png" + }, + { + "rel" : "http://webfinger.net/rel/profile-page", + "href" : "http://profile.example.com/u/smithj" + }, + { + "rel" : "http://packetizer.com/rel/blog", + "href" : "http://blog.example.com/smithj" + }, + { + "rel" : "http://specs.openid.net/auth/2.0/provider", + "href" : "https://profile.example.com/u/smithj" + }, + { + "rel" : "payment", + "href" : "https://www.paypal.me/JohnSmith" + } + ] +} diff --git a/assets/favicon.ico b/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..94e03f9eb3efbab27fee57b50430042bd3ae71c3 GIT binary patch literal 4286 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 h%ebcj + order deny,allow + deny from all + + + Options FollowSymlinks + AllowOverride All + Order allow,deny + Require all granted + + + order deny,allow + deny from all +