diff --git a/Layout/default/Preordercampaign/Form.php b/Layout/default/Preordercampaign/Form.php
index 3ac439e40..4a06e7d07 100644
--- a/Layout/default/Preordercampaign/Form.php
+++ b/Layout/default/Preordercampaign/Form.php
@@ -153,7 +153,7 @@
CORS Origin Hostnamen
- Website Url oder Hostname; ein Eintrag pro Zeile
+ Hostname der Website, mit oder ohne Protokoll (https:// ); *. als Wildcard erlaubt (*.domain.com ); ein Eintrag pro Zeile
diff --git a/application/Api/v1/AddressdbApicontroller.php b/application/Api/v1/AddressdbApicontroller.php
index 8a9d782ce..fb4a3e4ec 100644
--- a/application/Api/v1/AddressdbApicontroller.php
+++ b/application/Api/v1/AddressdbApicontroller.php
@@ -1,6 +1,7 @@
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() {
diff --git a/application/Api/v1/PreorderApicontroller.php b/application/Api/v1/PreorderApicontroller.php
new file mode 100644
index 000000000..4c522fc31
--- /dev/null
+++ b/application/Api/v1/PreorderApicontroller.php
@@ -0,0 +1,157 @@
+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;
+ }
+
+}
\ No newline at end of file
diff --git a/lib/mvcfronk/mfBase/mfBaseApicontroller.php b/lib/mvcfronk/mfBase/mfBaseApicontroller.php
index 4924594d9..7030566e6 100644
--- a/lib/mvcfronk/mfBase/mfBaseApicontroller.php
+++ b/lib/mvcfronk/mfBase/mfBaseApicontroller.php
@@ -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) {
diff --git a/public/.htaccess b/public/.htaccess
index f717870af..efdafabbc 100644
--- a/public/.htaccess
+++ b/public/.htaccess
@@ -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
diff --git a/public/docs/.htaccess b/public/docs/.htaccess
index 3f0414a6f..b767cba79 100644
--- a/public/docs/.htaccess
+++ b/public/docs/.htaccess
@@ -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"