Commit f94e38cd authored by Dominik Hebeler's avatar Dominik Hebeler

added process of changing key

parent c1f71151
......@@ -6,9 +6,14 @@ use Cookie;
use Illuminate\Http\Request;
use LaravelLocalization;
use \App\Models\Key;
use \Carbon\Carbon;
use Validator;
class KeyController extends Controller
{
// How many Ad Free searches should a user get max when he creates a new key
const KEYCHANGE_ADFREE_SEARCHES = 150;
public function index(Request $request)
{
$redirUrl = $request->input('redirUrl', "");
......@@ -46,10 +51,7 @@ class KeyController extends Controller
$settings = Cookie::get();
$settings['key'] = $keyToSet;
$cookieLink = LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('loadSettings', $settings));
return view('key')
->with('title', trans('titles.key'))
->with('cookie', $keyToSet)
->with('cookieLink', $cookieLink);
return redirect(LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('keyindex')));
} else {
$cookieLink = LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('loadSettings', Cookie::get()));
return view('key')
......@@ -74,4 +76,152 @@ class KeyController extends Controller
return redirect($url);
}
}
public function changeKeyIndex(\App\Models\Key $key, Request $request){
if(!$key->canChange()){
return redirect(LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('keyindex')));
}
return view('keychange', [
"title" => trans('titles.keychange'),
"key" => $key->key,
"css" => [mix('css/keychange/index.css')]
]);
}
public function removeCurrent(\App\Models\Key $key, Request $request){
if(!$key->canChange()){
return redirect(LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('keyindex')));
}
// Reduce Current Key
$res = $key->reduce(self::KEYCHANGE_ADFREE_SEARCHES);
if(empty($res) || empty($res->status) || $res->status !== "success"){
return redirect(LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('keyindex')));
}
// Redirect to Cookie Remove URL with redirect to step two
$validUntil = Carbon::now("Europe/London")->addDays(2);
$format = "Y-m-d H:i:s";
$data = [
"validUntil" => $validUntil->format($format),
"password" => hash_hmac("sha256", $validUntil->format($format), env("APP_KEY", "WEAK_KEY")),
];
$targetUrl = LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('changeKeyTwo', $data));
$redirUrl = LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('removeCookie', [
"ir" => $targetUrl
]));
return redirect($redirUrl);
}
public function generateNew(\App\Models\Key $key, Request $request){
// Validate Request Data
$validUntil = $request->input('validUntil', '');
$password = $request->input('password', '');
$format = "Y-m-d H:i:s";
// Check if Validuntil
$valid = true;
if(empty($validUntil)){
$valid = false;
}else{
$validUntil = Carbon::createFromFormat($format, $validUntil, "Europe/London");
if(!$validUntil){
$valid = false;
}
}
if($valid && Carbon::now()->diffInSeconds($validUntil) <= 0){
$valid = false;
}
if($valid){
// Check if hash matches
$expectedHash = hash_hmac("sha256", $validUntil->format($format), env("APP_KEY", "WEAK_KEY"));
if(!hash_equals($expectedHash, $password)){
$valid = false;
}
}
if(!$valid){
return redirect(LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('keyindex')));
}
// Check if the key already was generated
if (!$key->checkForChange("", $password)) {
return redirect(LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('keyindex')));
}
return view('keychangetwo', [
"title" => trans('titles.keychange'),
"validUntil" => $validUntil,
"css" => [mix('css/keychange/index.css')]
]);
}
public function generateNewPost(\App\Models\Key $key, Request $request){
// Validate Request Data
$validUntil = $request->input('validUntil', '');
$password = $request->input('password', '');
$format = "Y-m-d H:i:s";
// Check if Validuntil
$valid = true;
if(empty($validUntil)){
$valid = false;
}else{
$validUntil = Carbon::createFromFormat($format, $validUntil, "Europe/London");
if(!$validUntil){
$valid = false;
}
}
if($valid && Carbon::now()->diffInSeconds($validUntil) <= 0){
$valid = false;
}
if($valid){
// Check if hash matches
$expectedHash = hash_hmac("sha256", $validUntil->format($format), env("APP_KEY", "WEAK_KEY"));
if(!hash_equals($expectedHash, $password)){
$valid = false;
}
}
if(!$valid){
return redirect(LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('keyindex')));
}
$validator = Validator::make($request->all(), [
'newkey' => 'required|min:4|max:20',
]);
if($validator->fails()) {
$data = [
"validUntil" => $validUntil->format($format),
"password" => hash_hmac("sha256", $validUntil->format($format), env("APP_KEY", "WEAK_KEY")),
"newkey" => $request->input('newkey', ''),
];
$targetUrl = LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('changeKeyTwo', $data));
return redirect($targetUrl);
}
$newkey = $request->input('newkey', '');
$characters = '0123456789abcdefghijklmnopqrstuvwxyz';
$randomSuffix = "";
$suffixCount = 3;
for($i = 0; $i < $suffixCount; $i++){
$randomSuffix .= $characters[rand(0, strlen($characters)-1)];
}
$newkey = $newkey . $randomSuffix;
if($key->checkForChange($newkey, $password)){
$result = $key->generateKey(null, self::KEYCHANGE_ADFREE_SEARCHES, $newkey, "Schlüssel gewechselt. Hash $password");
if(!empty($result)){
Cookie::queue('key', $result, 525600, '/', null, false, false);
return redirect(LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('changeKeyThree', ["newkey" => $result])));
}
}
$data = [
"validUntil" => $validUntil->format($format),
"password" => hash_hmac("sha256", $validUntil->format($format), env("APP_KEY", "WEAK_KEY")),
];
$targetUrl = LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('changeKeyTwo', $data));
return redirect($targetUrl);
}
}
......@@ -220,7 +220,7 @@ class MailController extends Controller
$betrag = round($betrag, 2, PHP_ROUND_HALF_DOWN);
# Generating personalised key for donor
$key = app('App\Models\Key')->generateKey($betrag);
$key = app('App\Models\Key')->generateKey($betrag, null, null, 'Für ' . $betrag . '€ aufgeladen am '. date("d.m.Y"));
try {
$postdata = [
......
......@@ -121,15 +121,26 @@ class Key
return false;
}
}
public function generateKey($payment)
public function generateKey($payment = null, $adFreeSearches = null, $key = null, $notes = "")
{
$authKey = base64_encode(env("KEY_USER", "test") . ':' . env("KEY_PASSWORD", "test"));
$postdata = http_build_query(array(
'payment' => $payment,
$postdata = array(
'apiAccess' => 'normal',
'notes' => 'Fuer ' . $payment . '€ aufgeladen am '. date("d.m.Y"),
'expiresAfterDays' => 365
));
'expiresAfterDays' => 365,
'notes' => $notes
);
if(!empty($key)){
$postdata["key"] = $key;
}
if(!empty($payment)){
$postdata["payment"] = $payment;
}else if(!empty($adFreeSearches)){
$postdata["adFreeSearches"] = $adFreeSearches;
}else{
return false;
}
$postdata = http_build_query($postdata, "", "&", PHP_QUERY_RFC3986);
$opts = array(
'http' => array(
'method' => 'POST',
......@@ -153,6 +164,34 @@ class Key
}
}
public function reduce($count){
$authKey = base64_encode(env("KEY_USER", "test") . ':' . env("KEY_PASSWORD", "test"));
$postdata = http_build_query(array(
'adFreeSearches' => $count,
));
$opts = array(
'http' => array(
'method' => 'POST',
'header' => [
'Content-type: application/x-www-form-urlencoded',
'Authorization: Basic ' . $authKey
],
'content' => $postdata,
'timeout' => 5
),
);
$context = stream_context_create($opts);
try {
$link = $this->keyserver . "v2/key/" . $this->key . "/reduce-searches";
$result = json_decode(file_get_contents($link, false, $context));
return $result;
} catch (\ErrorException $e) {
return false;
}
}
/**
* Tells if this key is liable to change to a custom key
* Currently only members are allowed to do so and only every 2 days
......@@ -161,7 +200,7 @@ class Key
* @return boolean
*/
public function canChange(){
if(empty($this->status) || !preg_match("/^Mitgliederschlüssel\./", $this->keyinfo->notes)){
if(empty($this->status) || !preg_match("/^Mitgliederschlüssel\./", $this->keyinfo->notes) || $this->keyinfo->adFreeSearches < \App\Http\Controllers\KeyController::KEYCHANGE_ADFREE_SEARCHES){
return false;
}
if(!empty($this->keyinfo->KeyChangedAt)){
......@@ -175,4 +214,38 @@ class Key
}
return true;
}
public function checkForChange($newkey = "", $hash){
$authKey = base64_encode(env("KEY_USER", "test") . ':' . env("KEY_PASSWORD", "test"));
$postdata = http_build_query(array(
'hash' => $hash,
'key' => $newkey,
));
$opts = array(
'http' => array(
'method' => 'POST',
'header' => [
'Content-type: application/x-www-form-urlencoded',
'Authorization: Basic ' . $authKey
],
'content' => $postdata,
'timeout' => 5
),
);
$context = stream_context_create($opts);
try {
$link = $this->keyserver . "v2/key/can-change";
$result = json_decode(file_get_contents($link, false, $context));
if(!empty($result) && $result->status === "success" && empty($result->results)){
return true;
}else{
return false;
}
} catch (\ErrorException $e) {
return false;
}
}
}
<?php
return [
'h1' => 'Wunsch Schlüssel',
'p1' => 'Mit diesem Tool haben Sie die Möglichkeit Ihren aktuellen Mitgliederschlüssel ":key" zu wechseln. Mitgliederschlüssel sind an Ihre Mitgliedschaft im SUMA-EV gekoppelt. So kann dieser automatisch erneuert werden. Auch wenn wir das nicht tun, hätten wir dadurch natürlich theoretisch die Möglichkeit eine Verknüpfung zwischen Mitgliederschlüssel und durchgeführten Suchen zu schaffen. Mit dem Wechsel auf einen Wunsch Schlüssel wird eine solche Verknüpfung theoretisch und praktisch für uns unmöglich.',
'p2' => 'Außerdem können Sie mit diesem Tool einen Schlüssel erstellen, der leichter zu merken ist. Bitte heben Sie sich den ursprünglichen Schlüssel dennoch auf. Dadurch, dass der neue Schlüssel nicht mehr automatisch erneuert werden kann, wird dieser irgendwann ungültig. In dem Fall müssen Sie diesen Prozess wiederholen, indem Sie Ihren ursprünglichen Mitgliederschlüssel (:key) bei MetaGer eingeben und dieses Tool erneut aufrufen.',
'p3' => 'Heben Sie sich deshalb beide Schlüssel gut auf und beachten Sie insbesondere, dass wir nicht in der Lage sind Ihnen den neuen, durch diesen Wechsel entstandenen, Schlüssel bei Verlust mitzuteilen oder wiederherzustellen.',
'p4' => 'Dieses Tool führt Sie durch folgende Schritte um einen neuen anonymen Wunsch Schlüssel zu erstellen:',
'ol1.li1' => 'Entfernen aller Informationen über den aktuellen Mitgliederschlüssel aus Ihrem Browser',
'ol1.li2' => 'Erzeugen einer URL mit der ein neuer Schlüssel erstellt werden kann. Diese URL enthält keine Informationen mehr über den Mitgliederschlüssel.',
'ol1.li3' => 'Erzeugen eines neuen Wunsch Schlüssels',
'ol1.li4' => 'Speichern des neuen Wunsch Schlüssels im Browser',
'a1' => 'Gelesen - Wunsch Schlüssel erstellen',
'p5' => 'Ihr bisheriger Mitgliederschlüssel wurde nun aus dem Browser gelöscht. Außerdem haben wir eine URL erzeugt, mit der Sie sich Ihren Wunschschlüssel erstellen können. Dies ist innerhalb von :validUntil jederzeit und von jedem Gerät aus möglich indem Sie folgende URL im Browser aufrufen, oder direkt das Formular auf dieser Seite verwenden.',
'p6' => 'In das nachfolgende Textfeld können, Sie nun Ihren gewünschten Schlüssel eintragen. Um Überschneidungen zu vermeiden fügen wir noch ein paar Zeichen an den Schlüssel an. Mit dem Klick auf generieren wird Ihr neuer Schlüssel dann erzeugt und auf der folgenden Seite angezeigt, damit Sie Ihn sich abspeichern können.',
'input1label' => 'Wunsch Schlüssel eintragen (Mindestens 4 und maximal 20 Zeichen)',
'input1' => 'Wunsch Schlüssel eintragen',
'button1' => 'Wunsch Schlüssel generieren',
'p7' => ' Ihr neuer Wunsch Schlüssel wurde erfolgreich eingerichtet. Bitte merken Sie ihn sich gut. Er kann bei Verlust nämlich nicht wiederhergestellt werden. Er wird außerdem irgendwann ungültig werden. Wenn der Fall eintritt, tragen Sie bei MetaGer bitte Ihren regulären Mitgliederschlüssel ein und wiederholen Sie diesen Vorgang.',
'p8' => 'Ihr neuer Schlüssel lautet:',
];
\ No newline at end of file
......@@ -24,6 +24,7 @@ return [
'asso' => 'Assoziator - MetaGer',
'plugin' => 'Plugin - MetaGer',
'key' => 'Mitgliederschlüssel - MetaGer',
'keychange' => 'Mitgliederschlüssel wechseln',
'settings' => 'Sucheinstellungen (:fokus)',
'allSettings' => 'Alle Sucheinstellungen - MetaGer',
'transparency' => 'Transparenzerklärung - MetaGer',
......
<?php
return [
'h1' => 'Favorite Key',
'p1' => 'With this tool you have the possibility to change your current membership key ":key". Membership keys are linked to your membership in SUMA-EV. So it can be renewed automatically. Even if we don\'t do that, we would of course theoretically have the possibility of creating a link between the membership key and the searches carried out. With the change to a desired key, such a link is theoretically and practically impossible for us.',
'p2' => 'Also, you can use this tool to create a key that is easier to remember. Please keep the original key anyway. Because the new key can no longer be renewed automatically, it will eventually become invalid. In that case you will have to repeat this process by entering your original membership key (:key) into MetaGer and calling up this tool again.',
'p3' => 'You should therefore keep both keys in a safe place and pay particular attention to the fact that we are not able to restore the new one if it is lost.',
'p4' => 'This tool guides you through the following steps to create a new anonymous favorite key:',
'ol1.li1' => 'Remove all information about the current member key from your browser',
'ol1.li2' => 'Generating a URL with which a new key can be created. This URL no longer contains information about the member key.',
'ol1.li3' => 'Generate a new favorite key',
'ol1.li4' => 'Save the new favorite key in the browser',
'a1' => 'Read - create a favorite key',
'p5' => 'Your previous membership key has now been deleted from the browser. We have also created a URL that you can use to create your desired key. This is possible within :validUntil at any time and from any device by calling up the following URL in the browser, or directly using the form on this page.',
'p6' => 'You can now enter your desired key in the following text field. To avoid overlapping, we add a few more characters to the key. By clicking on generate, your new key will be generated and displayed on the following page so that you can save it.',
'input1label' => 'Enter the desired key (at least 4 and a maximum of 20 characters)',
'input1' => 'Enter the desired key',
'button1' => 'Generate favorite key',
'p7' => 'Your new desired key has been successfully set up. Please make a note of it. It cannot be restored if it is lost. It will also expire at some point. If this occurs, please enter your regular membership key at MetaGer and repeat this process.',
'p8' => 'Your new key is:',
];
\ No newline at end of file
#steps {
display: flex;
justify-content: space-between;
padding: 16px;
counter-reset: steps;
> div {
counter-increment: steps;
padding: 8px;
&::before {
content: counter(steps)". ";
}
&.active {
background-color: #2879025c;
}
}
}
\ No newline at end of file
......@@ -42,7 +42,7 @@
<div class="section">
<h3>@lang('key.custom.h3')</h3>
<p>@lang('key.custom.p1')</p>
<a href="#">@lang('key.custom.a1')</a>
<a href="{{ LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('changeKeyOne')) }}">@lang('key.custom.a1')</a>
</div>
@endif
<div class="section">
......
@extends('layouts.subPages', ['page' => 'key'])
@section('title', $title )
@section('content')
<div class="card-heavy" id="steps">
<div class="step-one active">Entfernen des aktuellen Schlüssels</div>
<div class="step-two">Generieren des neuen Schlüssels</div>
<div class="step-three">Speichern des neuen Schlüssels</div>
</div>
<div class="card-heavy">
<h1>@lang('keychange.h1')</h1>
<p>@lang('keychange.p1', ["key" => $key])</p>
<p>@lang('keychange.p2', ["key" => $key])</p>
<p>@lang('keychange.p3')</p>
<p>@lang('keychange.p4')</p>
<ol>
<li>@lang('keychange.ol1.li1')</li>
<li>@lang('keychange.ol1.li2')</li>
<li>@lang('keychange.ol1.li3')</li>
<li>@lang('keychange.ol1.li4')</li>
</ol>
<form action="" method="post">
<button class="btn btn-default">@lang('keychange.a1')</button>
</form>
</div>
@endsection
@extends('layouts.subPages', ['page' => 'key'])
@section('title', $title )
@section('content')
<div class="card-heavy" id="steps">
<div class="step-one">Entfernen des aktuellen Schlüssels</div>
<div class="step-two">Generieren des neuen Schlüssels</div>
<div class="step-three active">Speichern des neuen Schlüssels</div>
</div>
<div class="card-heavy">
<h1>@lang('keychange.h1')</h1>
<p>@lang('keychange.p7')</p>
<p>@lang('keychange.p8')</p>
<div class="copyLink">
<input id="searchString" class="loadSettings" type="text" value="{{ Request::input('newkey', '') }}" readonly>
<button class="js-only btn btn-default" onclick="var copyText = document.getElementById('searchString');copyText.select();copyText.setSelectionRange(0, 99999);document.execCommand('copy');">@lang('settings.copy')</button>
</div>
</div>
@endsection
@extends('layouts.subPages', ['page' => 'key'])
@section('title', $title )
@section('content')
<div class="card-heavy" id="steps">
<div class="step-one">Entfernen des aktuellen Schlüssels</div>
<div class="step-two active">Generieren des neuen Schlüssels</div>
<div class="step-three">Speichern des neuen Schlüssels</div>
</div>
<div class="card-heavy">
<h1>@lang('keychange.h1')</h1>
<p>@lang('keychange.p5', [
'validUntil' => trim(str_replace(" später", "", $validUntil->longRelativeDiffForHumans(Carbon::now("Europe/London"), 1)))
])</p>
<div class="copyLink">
<input id="searchString" class="loadSettings" type="text" value="{{ url()->full() }}" readonly>
<button class="js-only btn btn-default" onclick="var copyText = document.getElementById('searchString');copyText.select();copyText.setSelectionRange(0, 99999);document.execCommand('copy');">@lang('settings.copy')</button>
</div>
<br>
<p>@lang('keychange.p6')</p>
<form action="{{ LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('changeKeyTwo')) }}" method="post">
<input type="hidden" name="validUntil" value="{{ Request::input('validUntil', '') }}">
<input type="hidden" name="password" value="{{ Request::input('password', '') }}">
@if(Request::filled('newkey'))
<p style="color: red">@lang('keychange.input1label')</p>
@else
<p><label for="mewkey">@lang('keychange.input1label')</label></p>
@endif
<input type="text" name="newkey" id="newkey" required placeholder="@lang('keychange.input1')" value="{{ Request::input('newkey', '') }}">
<button type="submit" class="btn btn-default">@lang('keychange.button1')</button>
</form>
</div>
@endsection
......@@ -6,10 +6,27 @@ Route::group(
'middleware' => [ 'localeSessionRedirect', 'localizationRedirect' ]*/
],
function () {
Route::get('meta/key', "KeyController@index")->middleware('removekey');
Route::get('meta/key', "KeyController@index")->middleware('removekey')->name('keyindex');
Route::post('meta/key', 'KeyController@setKey');
Route::any('meta/key/remove', 'KeyController@removeKey')->name("removeCookie");
Route::group([
'prefix' => 'meta/key/change'
], function() {
// Routes to change the key for members
Route::get('remove-current', 'KeyController@changeKeyIndex')->middleware('removekey')->name('changeKeyOne');
Route::post('remove-current', 'KeyController@removeCurrent')->middleware('removekey');
Route::get('generate-new', 'KeyController@generateNew')->name('changeKeyTwo');
Route::post('generate-new', 'KeyController@generateNewPost');
Route::get('save-new', function(){
return view('keychangethree', ["title" => trans('titles.keychange'), "css" => [mix('css/keychange/index.css')]]);
})->name('changeKeyThree');
});
Route::group([
'prefix' => 'meta/settings',
],
......
......@@ -58,6 +58,9 @@ mix
.less("resources/less/metager/pages/spende/danke.less", "public/css/spende/danke.css", {
strictMath: true
})
.less("resources/less/metager/pages/keychange/index.less", "public/css/keychange/index.css", {
strictMath: true
})
// js
.babel(
[
......
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