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 0000000..94e03f9 Binary files /dev/null and b/assets/favicon.ico differ diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..13fe3f7 Binary files /dev/null and b/assets/logo.png differ diff --git a/assets/pews.conf b/assets/pews.conf new file mode 100644 index 0000000..8d1265a --- /dev/null +++ b/assets/pews.conf @@ -0,0 +1,16 @@ +### PEWS apache conf file ### +Alias /.well-known /var/www/PEWS/.well-known + + order deny,allow + deny from all + + + Options FollowSymlinks + AllowOverride All + Order allow,deny + Require all granted + + + order deny,allow + deny from all +