WIP Preorder 2022-09-14

This commit is contained in:
Frank Schubert
2022-09-14 16:52:00 +02:00
parent 8a67401d2c
commit 2cbdbf3813
6 changed files with 264 additions and 14 deletions

View File

@@ -153,7 +153,7 @@
<label class="col-lg-2 col-form-label" for="corsorigins">CORS Origin Hostnamen</label>
<div class="col-lg-10">
<textarea class="form-control" name="corsorigins"><?=($campaign->corsorigins) ? implode("\n", $campaign->corsorigins) : ""?></textarea>
<small>Website Url oder Hostname; ein Eintrag pro Zeile</small>
<small>Hostname der Website, mit oder ohne Protokoll (<em>https://</em>); *. als Wildcard erlaubt (<em>*.domain.com</em>); ein Eintrag pro Zeile</small>
</div>
</div>
</div>

View File

@@ -1,6 +1,7 @@
<?php
class AddressdbApicontroller extends mfBaseApicontroller {
private $filter_gemeinde_ids = [];
protected function init() {
$db = $this->db(ADDRESSDB_DBHOST, ADDRESSDB_DBUSER, ADDRESSDB_DBPASS, ADDRESSDB_DBNAME);
@@ -9,6 +10,24 @@ class AddressdbApicontroller extends mfBaseApicontroller {
$this->addRoute("/addressdb/findStreet", "findStreet", "POST");
$this->addRoute("/addressdb/findZip", "findZip", "POST");
$this->addRoute("/addressdb/findCity", "findCity", "POST");
$this->allowMissingOrigin = false;
}
protected function authenticated() {
$campaignApiuser = PreordercampaignApiuserModel::getFirst(["worker_id" => $this->me->id]);
$campaign = new Preordercampaign($campaignApiuser->preordercampaign_id);
if($campaign) {
foreach(PreordercampaignGemeindeModel::search(['preordercampaign_id' => $campaign->id]) as $gemeinde) {
$this->filter_gemeinde_ids[] = $gemeinde->id;
}
}
foreach(PreordercampaignOriginhostnameModel::search(['preordercampaign_id' => $campaign->id]) as $origin) {
$this->addAllowedOrigin($origin->hostname);
}
//var_dump($campaign, $this->allowed_origins);exit;
}
protected function findCity() {

View File

@@ -0,0 +1,157 @@
<?php
class PreorderApicontroller extends mfBaseApicontroller {
private $filter_gemeinde_ids = [];
private $campaign;
protected function init() {
$db = $this->db(ADDRESSDB_DBHOST, ADDRESSDB_DBUSER, ADDRESSDB_DBPASS, ADDRESSDB_DBNAME);
$this->addRoute("/preorder", "submitPreorder", "POST");
$this->allowMissingOrigin = false;
}
protected function authenticated() {
$campaignApiuser = PreordercampaignApiuserModel::getFirst(["worker_id" => $this->me->id]);
$campaign = new Preordercampaign($campaignApiuser->preordercampaign_id);
if($campaign) {
$this->campaign = $campaign;
foreach(PreordercampaignGemeindeModel::search(['preordercampaign_id' => $campaign->id]) as $gemeinde) {
$this->filter_gemeinde_ids[] = $gemeinde->id;
}
}
foreach(PreordercampaignOriginhostnameModel::search(['preordercampaign_id' => $campaign->id]) as $origin) {
$this->addAllowedOrigin($origin->hostname);
}
//var_dump($campaign, $this->allowed_origins);exit;
}
protected function submitPreorder() {
if(!$this->campaign) {
$this->log->debug("disallowed request because no campaign for apikey");
return mfResponse::Forbidden();
}
$type = $this->post['type'];
if($type != "provision" && $type != "order") {
return mfResponse::BadRequest(["message" => "Unknown type"]);
}
if(!array_key_exists("address", $this->post)) {
return mfResponse::BadRequest(['message' => "address missing"]);
}
if(!array_key_exists("customer", $this->post)) {
return mfResponse::BadRequest(['message' => "customer data missing"]);
}
// check address
if(!property_exists($this->post['address'],"street") ||
!property_exists($this->post['address'],"housenumber") ||
!property_exists($this->post['address'],"zip") ||
!property_exists($this->post['address'],"city")
) {
return mfResponse::BadRequest(['message' => "Mandatory address fields missing"]);
}
$address_search = [];
foreach(['street' => 'strasse','housenumber' => "hausnummer",'zip' => "plz",'city' => "ortschaft"] as $key => $field_name) {
if(property_exists($this->post['address'], $key)) {
$address_search[$field_name] = trim($this->post['address']->$key);
}
}
$unit_search = [];
foreach(['block','stiege','stock','tuer'] as $key) {
if(property_exists($this->post['address'], $key) && trim($this->post['address']->key)) {
$unit_search[$key] = trim($this->post['address']->$key);
}
}
// check customer
$customer = $this->post['customer'];
if(!property_exists($customer,"firstname") ||
!property_exists($customer,"lastname") ||
!property_exists($customer,"street") ||
!property_exists($customer,"zip") ||
!property_exists($customer,"city")
) {
return mfResponse::BadRequest(['message' => "Mandatory customer fields missing"]);
}
// search address in AddressDB
foreach($address_search as $field => $value) {
$where .= " AND `$field` = '$value'";
}
$sql = "SELECT * FROM view_hausnummer WHERE 1=1 $where";
$res = $this->db()->query($sql);
if(!$this->db()->num_rows($res)) {
//var_dump($this->db()->num_rows($res), $this->db()->fetch_object($res));
return mfResponse::NotFound(['message' => "Adresse nicht gefunden"]);
}
$address = $this->db()->fetch_object($res);
// search wohneinheit
$unit = false;
if(count($unit_search)) {
foreach($unit_search as $field => $value) {
if($field == "stock" || $field == "stiege") continue; // only check for block and tuer
$where .= " AND `$field` = '$value'";
}
$sql = "SELECT * FROM view_wohneinheit WHERE 1=1 $where AND hausnummer_id=".$address->hausnummer_id;
$res = $this->db()->query($sql);
if(!$this->db()->num_rows($res)) {
return mfResponse::NotFound(['message' => "Wohneinheit nicht gefunden"]);
}
$unit = $this->db()->fetch_object($res);
//var_dump($this->db()->num_rows($res), $this->db()->fetch_object($res));
}
$preorder_data = [];
$preorder_data['preordercampaign_id'] = $this->campaign->id;
$preorder_data['type'] = $type;
$preorder_data['adb_hausnummer_id'] = $address->hausnummer_id;
if($unit) {
$preorder_data['adb_wohneinheit_id'] = $unit->wohneinheit_id;
}
if($type == "provision") {
$product = $this->campaign->setup_products['provision'][0];
if($product) {
$preorder_data['setup_product_id'] = $product->id;
$preorder_data['price_setup'] = $product->price_setup;
}
}
foreach(['company','uid','firstname','lastname','street','zip','city','phone','email'] as $key) {
if(property_exists($customer, $key)) {
$preorder_data[$key] = $customer->$key;
}
}
var_dump($preorder_data);exit;
var_dump($this->post);exit;
}
}

View File

@@ -21,6 +21,8 @@ class mfBaseApicontroller {
protected $get = [];
protected $post = [];
protected $format = "default";
protected $allowed_origins = [];
protected $allowMissingOrigin = true;
private $http_method;
private $routes = [];
@@ -39,10 +41,22 @@ class mfBaseApicontroller {
if($this->requireAuth) {
$this->authenticateUser();
if(method_exists($this,"authenticated")) {
$this->authenticated();
}
}
// Apicontroller should add allowed hostnames with $this->addAllowedOrigin()
$this->createCorsHeaders();
// CORS preflight OPTIONS
if($this->http_method == "OPTIONS") {
// dont execute route, OPTIONS only requires CORS headers
$this->return(mfResponse::Ok());
}
// route to action
$this->route = $params['apicall'].(($params['apiparams']) ? $params['apiparams'] : "");
$this->route = $params['apicall'].((array_key_exists("apiparams", $params)) ? $params['apiparams'] : "");
$responseData = $this->runRoute($this->route);
if(!$responseData) {
@@ -104,13 +118,6 @@ class mfBaseApicontroller {
$this->return(mfResponse::ImATeaPot());
}
// CORS preflight OPTIONS
// CORS headers must be correctly set in .htaccess or vhost config
if($this->http_method == "OPTIONS") {
// XXX TODO: api endpoint should decide if Origin should be allowed access
$this->return(mfResponse::Ok());
}
// POST Request
$post = [];
if($this->http_method == "POST") {
@@ -210,6 +217,67 @@ class mfBaseApicontroller {
}
private function createCorsHeaders() {
header("Access-Control-Allow-Methods: GET,POST,OPTIONS");
header("Access-Control-Allow-Headers: X-Api-Key");
//var_dump($this->headers);exit;
if(!is_array($this->allowed_origins) || !count($this->allowed_origins)) {
return true;
}
if(!array_key_exists("origin", $this->headers)) {
if(!$this->allowMissingOrigin) {
$this->return(mfResponse::Forbidden());
}
return true;
}
$request_origin = ["proto" => false, "hostname" => ""];
$m = [];
if(preg_match('#^(https?)://(.+)(:\d+)?$#i', $this->headers['origin'], $m)) {
$request_origin['proto'] = $m[1];
$request_origin['hostname'] = $m[2];
}
//var_dump($request_origin);exit;
foreach($this->allowed_origins as $origin) {
//echo $origin." -> ".$_SERVER["HTTP_HOST"];
$proto = false;
$hostname = $origin;
$m = [];
if(preg_match('#^(https?)://(.+)$#i', $origin, $m)) {
$proto = $m[1];
$hostname = $m[2];
}
if(substr($hostname, 0, 2) == "*.") {
$hostname = str_replace("*.", ".*\\.", $hostname);
}
//var_dump($hostname);exit;
//if($hostname == $request_origin['hostname']) {
if(preg_match('/^'.$hostname.'$/', $request_origin['hostname'])) {
if($proto) {
if($proto == $request_origin['proto']) {
header("Access-Control-Allow-Origin: $proto://".$request_origin['hostname']);
return true;
}
} else {
header("Access-Control-Allow-Origin: ".$request_origin['proto']."://".$request_origin['hostname']);
return true;
}
}
}
if(!$this->allowMissingOrigin) {
$this->return(mfResponse::Forbidden());
exit;
}
return true;
}
protected function runRoute($params) {
if(!is_array($this->routes) || !count($this->routes)) {
return false;
@@ -303,6 +371,12 @@ class mfBaseApicontroller {
"action" => $action,
"method" => $method
];
return true;
}
protected function addAllowedOrigin($origin) {
$this->allowed_origins[] = trim($origin);
return true;
}
public static function loadApiClass($name) {

View File

@@ -1,7 +1,7 @@
SetEnvIf Origin "^(https://docs.thetool.xinon.at|https://editor.swagger.io|.*abstellgleis.at)$" AccessControlAllowOrigin=$0
Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Header add Access-Control-Allow-Methods: "GET,POST,OPTIONS"
Header add Access-Control-Allow-Headers: "X-Api-Key"
#SetEnvIf Origin "^(https://docs.thetool.xinon.at|https://editor.swagger.io|.*abstellgleis.at)$" AccessControlAllowOrigin=$0
#Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
#Header add Access-Control-Allow-Methods: "GET,POST,OPTIONS"
#Header add Access-Control-Allow-Headers: "X-Api-Key"
RewriteEngine on

View File

@@ -3,4 +3,4 @@ Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessCo
Header add Access-Control-Allow-Methods: "GET,POST,OPTIONS"
Header add Access-Control-Allow-Headers: "X-Api-Key"
Header add Cach-control: "no-store, no-cache, must-revalidate"
Header add Cache-control: "no-store, no-cache, must-revalidate"