Commit 238ca717 authored by Michael Ochmann's avatar Michael Ochmann

initial commit

parents
.idea
settings.ini
RewriteEngine On
RewriteBase /fsi/shorturl/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([\w]+)/? index.php?shortcode=$1
<FilesMatch "\.(?:ini)$">
Order allow,deny
Deny from all
</FilesMatch>
\ No newline at end of file
<?php
namespace FSI;
class Authenticator {
const URL = "https://fsi.hochschule-trier.de/shibboleth";
private $shibService;
private $root;
private $settings;
private $challenge;
private $user;
private $authenticated;
public function __construct(URLShortener $root) {
$this->root = $root;
$this->settings = SettingsHandler::Instance();
if ($this->settings->system->debug) {
$this->authenticated = true;
$this->user = "heinz";
return;
}
$this->shibService = new ShibbolethService("/etc/letsencrypt/live/fsi.rocks/privkey.pem");
if (!isset($_SESSION["challenge"])) {
$this->challenge = uniqid("", true);
$_SESSION["challenge"] = $this->challenge;
}
else {
$this->challenge = $_SESSION["challenge"];
}
if (isset($_GET["exchange"]))
$this->setUser();
if (isset($_SESSION["username"])) {
$this->user = $_SESSION["username"];
$this->authenticated = true;
}
else
$this->authenticated = false;
}
public function authenticated() {
return $this->authenticated;
}
public function user() {
return $this->user;
}
public function sendRequest() {
$obj = new \stdClass();
$obj->challenge = $this->challenge;
header("Location: ".ShibbolethService::SERVER_URL."/?exchange=".self::pack($this->shibService->crypt(json_encode($obj))));
}
private static function pack($data) {
return rawurlencode(base64_encode($data));
}
private static function unpack($data) {
return rawurldecode(base64_decode($data));
}
private function setUser() {
$data = json_decode($this->shibService->decrypt(rawurldecode(base64_decode($_GET["exchange"]))));
if ($data->challenge != $this->challenge) {
header("HTTP/1.0 401 Unauthorized");
$template = new Template("401.php");
$template->render();
exit;
}
$this->user = $data->username;
$this->authenticated = true;
$_SESSION["username"] = $data->username;
header("Location: ./");
}
}
<?php
namespace FSI;
class DatabaseHandler extends \mysqli {
private static $Instance;
private $settings;
protected function __construct() {
$this->settings = SettingsHandler::Instance()->database;
parent::__construct($this->settings->host, $this->settings->user, $this->settings->password, $this->settings->database);
$q = $this->query("sdsds");
}
public static function Instance() {
if (DatabaseHandler::$Instance === null)
DatabaseHandler::$Instance = new DatabaseHandler();
return DatabaseHandler::$Instance;
}
}
\ No newline at end of file
<?php
namespace FSI;
class Frontend {
private $root;
private $template;
private $db;
private $settings;
public function __construct(URLShortener $root) {
$this->root = $root;
$this->db = DatabaseHandler::Instance();
$this->settings = SettingsHandler::Instance();
$this->template = new Template("main.php");
if (isset($_POST["new"]) && $this->root->authenticator->authenticated())
new PostHandler($this->root);
}
public function render() {
if ($this->root->authenticator->authenticated()) {
$template = new Template("submitForm.php");
$userLabel = "<span class='glyphicon glyphicon-user'></span> eingeloggt als <b>".$this->root->authenticator->user()."</b>";
$links = [];
$query = $this->db->query("SELECT * FROM urls WHERE rft = '".$this->root->authenticator->user()."'");
while ($url = $query->fetch_object()) {
array_push($links, $url);
}
$template->setValues([
"links" => $links,
"baseurl" => $this->settings->system->baseurl
]);
}
else {
$template = new Template("login.php");
$userLabel = "";
}
$this->template->setValues([
"content" => $template->render(false),
"user" => $userLabel,
]);
$this->template->render();
}
}
\ No newline at end of file
<?php
namespace FSI;
class PostHandler {
private $root;
private $db;
private $settings;
public function __construct(URLShortener $root) {
$this->root = $root;
$this->db = DatabaseHandler::Instance();
$this->settings = SettingsHandler::Instance();
$this->add();
}
private function add() {
if (!isset($_POST["url"]) || $_POST["url"] == "")
return;
$shortcode = $_POST["shorturl"] == "" ? $this->generateShortcode() : $_POST["shorturl"];
$query = $this->db->prepare("INSERT INTO urls (url, shortcode, rft) VALUES(?, ?, ?)");
$query->bind_param(
"sss",
$_POST["url"],
$shortcode,
$this->root->authenticator->user()
);
$query->execute();
header("Location: ./");
}
private function generateShortcode() {
$code = substr(hash("md5", uniqid('', true)), 0, $this->settings->system->shortcodeLength);
$query = $this->db->query("SELECT COUNT(*) as amount FROM urls WHERE shortcode = '$code'");
$result = $query->fetch_object();
if ($result->amount == 0)
return $code;
else
return $this->generateShortcode();
}
}
\ No newline at end of file
<?php
namespace FSI;
class Redirector {
private $db;
public function __construct() {
$this->db = DatabaseHandler::Instance();
$this->redirect();
exit;
}
private function redirect() {
$shortcode = $_GET["shortcode"];
$id2 = null;
$visits = 0;
$redirect = "";
$query = $this->db->prepare("SELECT id, url, visited FROM urls WHERE shortcode = ? LIMIT 1");
$query->bind_param(
"s",
$shortcode
);
$query->execute();
if ($query->num_rows == 0) {
header("Location: ./", 404);
exit;
}
$query->bind_result($id, $url, $visited);
while ($query->fetch()) {
$id2 = $id;
$visits = (int) $visited + 1;
$redirect = $url;
}
$query = $this->db->prepare("UPDATE urls SET visited = ? WHERE id = ?");
$query->bind_param(
"ii",
$visits,
$id2
);
$query->execute();
header("Location: ".$redirect, 302);
}
}
\ No newline at end of file
<?php
namespace FSI;
class SettingsHandler {
private static $Instance = null;
protected function __construct() {
$settings = parse_ini_file(dirname(__FILE__)."/../settings.ini", true);
foreach ($settings as $section => $children) {
if (!array_key_exists($section, get_object_vars($this)))
$this->$section = new \stdClass();
foreach ($children as $key => $value) {
if (!array_key_exists($key, get_object_vars($this->$section)))
$this->$section->$key = new \stdClass();
$this->$section->$key = $value;
}
}
}
public static function Instance() {
if (SettingsHandler::$Instance === null)
SettingsHandler::$Instance = new SettingsHandler();
return SettingsHandler::$Instance;
}
}
\ No newline at end of file
<?php
namespace FSI;
class ShibbolethService {
const SERVER_URL = "https://fsi.hochschule-trier.de/shibboleth/ttt.php";
private $privateKey;
private $remoteURL;
public function __construct($privateKey, $remoteURL = null) {
$this->remoteURL = $remoteURL;
$this->privateKey = openssl_pkey_get_private("file://".$privateKey);
}
public function decrypt($data) {
$value = openssl_private_decrypt($data, $decryptedData, $this->privateKey);
return $decryptedData;
}
public function crypt($data) {
if ($this->remoteURL === null)
$publicKey = self::getPublicKey(self::SERVER_URL);
else
$publicKey = self::getPublicKey($this->remoteURL);
openssl_public_encrypt($data, $encryptedData, $publicKey);
return $encryptedData;
}
private static function getPublicKey($url) {
$context = stream_context_create(array("ssl" => array("capture_peer_cert" => true)));
$filepointer = fopen(self::getRootURL($url), "rb", false, $context);
$params = stream_context_get_params($filepointer);
return openssl_pkey_get_public($params["options"]["ssl"]["peer_certificate"]);
}
private static function getRootURL($url) {
$url = parse_url($url);
return $url["scheme"]."://".$url["host"];
}
}
<?php
namespace FSI;
class LoopElement {
public $key;
public $value;
public $source;
public $loopHTML;
public function __construct($key, $value, $source, $loopHTML) {
$this->key = $key;
$this->value = $value;
$this->source = $source;
$this->loopHTML = $loopHTML;
}
}
class Template {
private $source;
private $values;
private $loops = [];
public function __construct($sourceFile) {
if (!$this->source = @file_get_contents("templates/".$sourceFile))
throw new \Exception("Template file could not be loaded.", 501);
}
public function setValues($values) {
$this->values = $values;
}
private function replace() {
$this->getLoops();
foreach ($this->values as $key => $value) {
if (array_key_exists($key, $this->loops))
continue;
$this->source = str_replace("{{".$key."}}", $value, $this->source);
}
}
private function getLoops() {
$loops = [];
preg_match_all("/{%\s*for\s*([a-zA-Z0-9]+)\s*in\s*([a-zA-Z0-9]+)\s*%}(((?!{%\s*endfor\s*%}).)+){%\s*endfor\s*%}/ms", $this->source, $loops, PREG_SET_ORDER);
foreach ($loops as $loop) {
$object = new LoopElement($loop[1], $loop[2], $loop[0], $loop[3]);
$this->loops[$object->value] = $object;
}
foreach ($this->loops as $key => $value) {
if (!array_key_exists($key, $this->values) || !is_array($this->values[$key]))
continue;
$html = "";
foreach ($this->values[$key] as $item) {
if (is_object($item)) {
$snippet = $value->loopHTML;
$vars = array_keys(get_object_vars($item));
foreach ($vars as $var) {
$snippet = str_replace("{{".$value->key.'.'.$var."}}", $item->$var, $snippet);
}
$html .= $snippet;
}
else
$html .= str_replace("{{".$value->key."}}", $item, $value->loopHTML);
}
$this->source = str_replace($value->source, $html, $this->source);
}
}
public function render($display = true) {
if (is_array($this->values))
$this->replace();
if ($display)
echo $this->source;
else
return $this->source;
}
}
<?php
namespace FSI;
class URLShortener {
public $authenticator;
private $frontend;
public function __construct() {
spl_autoload_register([$this, "autoload"]);
if (isset($_GET["shortcode"]))
new Redirector();
$this->authenticator = new Authenticator($this);
if (isset($_GET["auth"]))
$this->authenticator->sendRequest();
$this->frontend = new Frontend($this);
$this->frontend->render();
}
public function autoload($className) {
$className = explode("\\", $className);
$className = $className[count($className) - 1];
if (file_exists(dirname(__FILE__)."/$className.php"))
require_once(dirname(__FILE__)."/$className.php");
}
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
@import url("bootstrap.min.css");
@font-face {
font-family: "Linux Libertine";
src: url("../fonts/linux_libertine.otf");
}
body {
background: #005196;
color: white;
}
h1 {
font-family: "Linux Libertine", serif;
}
input {
font-size: x-large !important;
height: auto !important;
}
main {
text-align: center;
}
table.table-list {
background: white !important;
color: #333;
}
table.table-list tr th:first-child {
width: 50%;
text-align: left !important;
}
table.table-list tr td {
text-align: left !important;
}
table.table-list tr td input {
font-size: medium !important;
font-weight: bold;
padding: 0 8px;
}
.container {
width: 50%;
}
.input-group-addon {
font-size: large;
}
.logo {
padding: 20px;
}
.logo img {
width: 40%;
}
.user {
text-align: left;
position: absolute;
left: 1%;
top: 3%;
color: #bbb;
}
@media(max-width:991px) {
.container {
width: 95%;
}
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
<?php
session_start();
define("DEBUG", true);
require_once("classes/URLShortener.php");
new \FSI\URLShortener();
\ No newline at end of file
[system]
shortcodeLength = 6
debug = false
baseurl = https://fsi.rocks
[database]
host = localhost
user = root
password = 1234
database = shorturl
<h1>Authorization Required</h1>
<p>
This server could not verify that you are authorized to access the document requested.
It looks like you were trying to fake a challenge ID. Please stop it!
</p>
<p>
Additionally, a 401 Authorization Required error was encoutered while trying to use an ErrorDocument to
handle the request.
</p>
<p>
<hr />
</p>
<i>Apache/1/2/3 NGinX/12,123 Node.js/Who knows? No server-information or fucks given.</i>
<a class="btn btn-lg btn-success" href="./?auth=true">Einloggen</a>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/fsirocks.css" />
<meta name="viewport" content="width=device-width" />
<title>URL-shortener – fsi.rocks</title>
</head>
<body>
<main class="container">
<section class="user">{{user}}</section>
<h1>fsi.rocks</h1>
<h2 class="logo"><img src="images/fsi_rocks_logo.svg" alt="fsi.rocks - url shortener – Fachschaft Informatik"></h2>
{{content}}
</main>
</body>
</html>
\ No newline at end of file
<form action="./" method="post">
<div class="form-group">
<p>
<input type="text" class="form-control" name="url" placeholder="URL to shorten">
</p>
<p>
<div class="input-group">
<div class="input-group-addon">fsi.rocks/</div>
<input type="text" class="form-control" name="shorturl" placeholder="your Shortcode (leave empty for random string - max. 20 characters)">
</div>
</p>
<p>
<button class="btn btn-lg btn-success">create</button>
</p>
</div>
<input type="hidden" name="new" value="true" />
</form>
<h2 style="margin-top: 50px;">Deine Links</h2>
<table class="table table-list">
<tr>
<th>URL</th>
<th>Shortlink</th>
</tr>
{% for link in links %}
<tr>
<td><a href="{{link.url}}">{{link.url}}</a></td>
<td><input type="text" value="{{baseurl}}/{{link.shortcode}}" disabled></td>
</tr>
{% endfor %}
</table>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment