├── .htaccess ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── LICENSE.3RDPARTY ├── README.md ├── config └── fsl_config.php ├── controllers ├── deck │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── deck │ └── kong.yaml └── fsl_controllers.php ├── examples ├── dadjokes.yaml └── docker-compose.yaml ├── index.php ├── lib ├── 3rd_party_helpers │ ├── Medoo.php │ ├── jwt_helper.php │ ├── password_helper.php │ └── xss_filter.class.php ├── fsl.php ├── fsl_functions.php └── limonade │ ├── public │ ├── css │ │ └── screen.css │ └── img │ │ ├── 404.gif │ │ └── fsl_logo.png │ └── views │ ├── _debug.html.php │ ├── _notices.html.php │ ├── default_layout.php │ └── error.html.php ├── public ├── banner-fsl.png ├── favicon.ico ├── fsl.jpeg ├── fsl_logo.png ├── kongmap2.png ├── kongmap3.png ├── launchpage.png ├── soda_glass.jpg └── soda_glass.thb.jpg ├── screenshots ├── kongmap-deck.png ├── kongmap-endpoint.png └── kongmap-home.png └── views ├── fsl_default_layout.php └── mapview.php /.htaccess: -------------------------------------------------------------------------------- 1 | 2 | Options +FollowSymlinks 3 | Options +Indexes 4 | RewriteEngine on 5 | 6 | # set directory to FSL Install if your app is in a subfolder else set this to / 7 | RewriteBase / 8 | 9 | # test string is a valid files 10 | RewriteCond %{SCRIPT_FILENAME} !-f 11 | # test string is a valid directory 12 | RewriteCond %{SCRIPT_FILENAME} !-d 13 | 14 | RewriteRule ^(.*)$ index.php?uri=/$1 [NC,L,QSA] 15 | # with QSA flag (query string append), 16 | # forces the rewrite engine to append a query string part of the 17 | # substitution string to the existing string, instead of replacing it. 18 | 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 20210706 2 | ### Added 3 | - None 4 | ### Changed 5 | - Updated: misc UI change to map view 6 | - Fixed: Navbar issues. Updated style. 7 | ### Removed 8 | - None 9 | 10 | 11 | ## 20210706 12 | ### Added 13 | - None 14 | ### Changed 15 | - Fixed: Changed default ports to high ports. Docker image no longer uses ports that require root privilege to run - https://github.com/yesinteractive/kong-map/issues/20 16 | - Fixed: Mixed content on view/edit cluster - https://github.com/yesinteractive/kong-map/issues/15 17 | - Fixed: fsl_curl return 0 when Kong admin port is https - https://github.com/yesinteractive/kong-map/issues/19 18 | - Updated Kong decK version to 1.7.0. 19 | ### Removed 20 | - None 21 | 22 | 23 | ## 20210329 24 | ### Added 25 | - None 26 | ### Changed 27 | - Updated Kong decK version to 1.5.1. 28 | ### Removed 29 | - None 30 | 31 | 32 | ## 20210120 33 | ### Added 34 | - None 35 | ### Changed 36 | - Fixed: Pop-ups don't show content correctly in map view: https://github.com/yesinteractive/kong-map/issues/10 37 | ### Removed 38 | - None 39 | 40 | ## 20201218 41 | ### Added 42 | - None 43 | ### Changed 44 | - Fixed Declarative configs were not displaying in editor for Kong Enterprise 2.2 on the default workspace. This has been resolved. 45 | ### Removed 46 | - None 47 | 48 | ## 20201109 49 | ### Added 50 | - Counts for Kong entities (services, routes, plugins) on Cluster Map cluster entity. 51 | - Route Analyzer now shows upstream targets configured for a service. 52 | ### Changed 53 | - Fixed Export Config with ID button function. Both buttons were previously exporting without ID's only. 54 | ### Removed 55 | - None 56 | 57 | 58 | ## 20201106 59 | ### Added 60 | - Ability to edit specific routes, plugins, and services directly from cluster map view. Click on map node 61 | to reveal the edit button. Clicking the edit button will open the config editor and highlight the element to be directly edited in the config. 62 | - Ability to edit specific routes, plugins, and services in route analyzer. Clicking the edit button 63 | will open the config editor and highlight the element to be directly edited in the config. 64 | - Toggle to view declarative config with or with entity ID's 65 | ### Changed 66 | - The `kong_ent_manager_url` JSON parameter when set to `null` will hide the Kong Manager buttons 67 | form KongMap for that cluster, even though the cluster is an enterprise cluster. 68 | - Export buttons to offer options to export with or without entity ID's. 69 | ### Removed 70 | - Kong Logo from KongMap header. 71 | 72 | 73 | ## 20201101 74 | ### Added 75 | - Docker compose install examples. 76 | - Tags, which are now searchable, to all routes and services nodes in cluster map. 77 | ### Changed 78 | - Filter in cluster view can search and filter to all details of an element of map such as tag name, or url or a service, etc. Previously could only filter on node name. 79 | - Fixed word wrap issues in web app view template. 80 | - Added Kong, Inc. trademark disclaimers. 81 | - Other misc page formatting. 82 | ### Removed 83 | -None 84 | 85 | 86 | ## 20201026 87 | ### Added 88 | - Confirmation dialog when about to save new declarative configuration. 89 | - Enhancement Request - Make map searchable. Will enhance this feature further in future. For now acts as a filter rather than proper search and highlight. https://github.com/yesinteractive/kong-map/issues/2 90 | 91 | ### Changed 92 | - Altered cluster details displayable data in cluster view 93 | - Altered key value data appearance in cluster/map view 94 | 95 | ### Removed 96 | - None 97 | 98 | 99 | ## 20201022 100 | ### Added 101 | - None 102 | 103 | ### Changed 104 | - Fixed - Custom plugin throws error when analyzing endpoint]) https://github.com/yesinteractive/kong-map/issues/1 105 | - Updated Config Editor buttons and restyled some UI elements 106 | 107 | ### Removed 108 | - None 109 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.11 2 | MAINTAINER YesInteractive- http://yes-interactive.com 3 | 4 | # Install modules and updates 5 | RUN apk update \ 6 | && apk --no-cache add \ 7 | openssl \ 8 | apache2 \ 9 | apache2-ssl \ 10 | apache2-http2 \ 11 | git \ 12 | unzip \ 13 | # Install PHP from community 14 | && apk --no-cache --repository http://dl-4.alpinelinux.org/alpine/v3.11/community/ add \ 15 | php7=="7.3.22-r0" \ 16 | php7-apache2 \ 17 | php7-bcmath \ 18 | php7-bz2 \ 19 | php7-calendar \ 20 | php7-common \ 21 | php7-ctype \ 22 | php7-curl \ 23 | php7-dom \ 24 | php7-json \ 25 | php7-mbstring \ 26 | php7-mcrypt \ 27 | php7-memcached \ 28 | php7-mysqlnd \ 29 | php7-opcache \ 30 | php7-openssl \ 31 | php7-pdo \ 32 | php7-pdo_mysql \ 33 | php7-pdo_sqlite \ 34 | php7-phar \ 35 | php7-session \ 36 | php7-sockets \ 37 | php7-xml \ 38 | php7-xmlreader \ 39 | && rm /var/cache/apk/* \ 40 | 41 | # Run required config / setup for apache 42 | # Ensure apache can create pid file 43 | #&& mkdir /run/apache2 \ 44 | # Fix group 45 | && sed -i -e 's/Group apache/Group www-data/g' /etc/apache2/httpd.conf \ 46 | # Fix ssl module 47 | && sed -i -e 's/LoadModule ssl_module lib\/apache2\/mod_ssl.so/LoadModule ssl_module modules\/mod_ssl.so/g' /etc/apache2/conf.d/ssl.conf \ 48 | && sed -i -e 's/LoadModule socache_shmcb_module lib\/apache2\/mod_socache_shmcb.so/LoadModule socache_shmcb_module modules\/mod_socache_shmcb.so/g' /etc/apache2/conf.d/ssl.conf \ 49 | # Enable modules 50 | && sed -i -e 's/#LoadModule rewrite_module modules\/mod_rewrite.so/LoadModule rewrite_module modules\/mod_rewrite.so/g' /etc/apache2/httpd.conf \ 51 | # Change document root to /app 52 | && mkdir /app && chown -R apache:apache /app \ 53 | && sed -i -e 's/\/var\/www\/localhost\/htdocs/\/app/g' /etc/apache2/httpd.conf \ 54 | && sed -i -e 's/\/var\/www\/localhost\/htdocs/\/app/g' /etc/apache2/conf.d/ssl.conf \ 55 | # moved apache logging to stdout and stderr 56 | && sed -i -e 's/ErrorLog logs\/error.log/ErrorLog \/dev\/stderr/g' /etc/apache2/httpd.conf \ 57 | && sed -i -e 's/CustomLog logs\/access.log combined/CustomLog \/dev\/stdout common/g' /etc/apache2/httpd.conf \ 58 | && sed -i -e 's/ErrorLog logs\/ssl_error.log/ErrorLog \/dev\/stderr/g' /etc/apache2/conf.d/ssl.conf \ 59 | && sed -i -e 's/TransferLog logs\/ssl_access.log/TransferLog \/dev\/stdout/g' /etc/apache2/conf.d/ssl.conf \ 60 | && sed -i -e 's/CustomLog logs\/ssl_request.log/#CustomLog \/dev\/stdout/g' /etc/apache2/conf.d/ssl.conf \ 61 | # Change default ports 62 | && sed -i -e 's/Listen 80/Listen 8100/g' /etc/apache2/httpd.conf \ 63 | && sed -i -e 's/443/8143/g' /etc/apache2/conf.d/ssl.conf \ 64 | # Allow for custom apache configs 65 | && mkdir /etc/apache2/conf.d/custom \ 66 | && echo '' >> /etc/apache2/httpd.conf \ 67 | && echo 'IncludeOptional /etc/apache2/conf.d/custom/*.conf' >> /etc/apache2/httpd.conf \ 68 | # Fix modules 69 | && sed -i 's#AllowOverride None#AllowOverride All#' /etc/apache2/httpd.conf \ 70 | && sed -i -e 's/ServerRoot \/var\/www/ServerRoot \/etc\/apache2/g' /etc/apache2/httpd.conf \ 71 | && mv /var/www/modules /etc/apache2/modules \ 72 | && mv /var/www/run /etc/apache2/run \ 73 | && mv /var/www/logs /etc/apache2/logs \ 74 | # Empty /var/www and add an index.php to show phpinfo() 75 | && rm -rf /var/www/* \ 76 | && echo '' > /app/phpinfo.php \ 77 | && wget --header '' https://github.com/yesinteractive/kong-map/archive/main.zip -P /app \ 78 | && unzip /app/main.zip -d /app \ 79 | && rm -rf /app/main.zip \ 80 | && cp -r /app/kong-map-main/. /app \ 81 | && rm -rf /app/kong-map-main \ 82 | && chmod 777 /app/controllers/deck/kong.yaml 83 | 84 | WORKDIR /app 85 | 86 | # Export http and https 87 | EXPOSE 8100 8143 88 | 89 | # Run apache in foreground 90 | CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"] 91 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 yes!nteractive, llc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.3RDPARTY: -------------------------------------------------------------------------------- 1 | KongMap is made possible by the following open source projects: 2 | 3 | =============================================== 4 | =============================================== 5 | =============================================== 6 | FSL 7 | https://github.com/yesinteractive/fsl 8 | * 9 | * @author Fabrice Luraine 10 | * @copyright Copyright (c) 2018 Yes Interactive, LLC 11 | * @license http://opensource.org/licenses/mit-license.php The MIT License 12 | * @package limonade 13 | 14 | MIT License 15 | 16 | Copyright (c) 2018 Yes Interactive, LLC 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | =============================================== 25 | =============================================== 26 | =============================================== 27 | L I M O N A D E 28 | https://github.com/sofadesign/limonade 29 | * 30 | * @author Fabrice Luraine 31 | * @copyright Copyright (c) 2009 Fabrice Luraine 32 | * @license http://opensource.org/licenses/mit-license.php The MIT License 33 | * @package limonade 34 | 35 | The MIT License (MIT) 36 | 37 | Copyright (c) 2009 Fabrice Luraine 38 | 39 | Permission is hereby granted, free of charge, to any person 40 | obtaining a copy of this software and associated documentation 41 | files (the "Software"), to deal in the Software without 42 | restriction, including without limitation the rights to use, 43 | copy, modify, merge, publish, distribute, sublicense, and/or sell 44 | copies of the Software, and to permit persons to whom the 45 | Software is furnished to do so, subject to the following 46 | conditions: 47 | 48 | The above copyright notice and this permission notice shall be 49 | included in all copies or substantial portions of the Software. 50 | 51 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 52 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 53 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 54 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 55 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 56 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 57 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 58 | OTHER DEALINGS IN THE SOFTWARE. 59 | 60 | =============================================== 61 | =============================================== 62 | =============================================== 63 | PHP XSS Filter 64 | https://github.com/JBlond/PHP-XSS-Filter 65 | * 66 | * @author mario.brandt 67 | * @copyright Copyright (c) 2013 - 2017 68 | 69 | The MIT License (MIT) 70 | 71 | Copyright (c) 2013 - 2017 Mario 72 | 73 | Permission is hereby granted, free of charge, to any person obtaining a copy of 74 | this software and associated documentation files (the "Software"), to deal in 75 | the Software without restriction, including without limitation the rights to 76 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 77 | the Software, and to permit persons to whom the Software is furnished to do so, 78 | subject to the following conditions: 79 | 80 | The above copyright notice and this permission notice shall be included in all 81 | copies or substantial portions of the Software. 82 | 83 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 84 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 85 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 86 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 87 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 88 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 89 | 90 | =============================================== 91 | =============================================== 92 | =============================================== 93 | Popper.js 94 | https://github.com/FezVrasta/popper.js 95 | * 96 | * @fileOverview Kickass library to create and place poppers near their reference elements. 97 | * @version ${require('./package.json').version} 98 | * @license 99 | * Copyright (c) 2016 Federico Zivolo and contributors 100 | 101 | The MIT License (MIT) 102 | ===================== 103 | 104 | Copyright © 2016 Federico Zivolo and contributors 105 | 106 | Permission is hereby granted, free of charge, to any person 107 | obtaining a copy of this software and associated documentation 108 | files (the “Software”), to deal in the Software without 109 | restriction, including without limitation the rights to use, 110 | copy, modify, merge, publish, distribute, sublicense, and/or sell 111 | copies of the Software, and to permit persons to whom the 112 | Software is furnished to do so, subject to the following 113 | conditions: 114 | 115 | The above copyright notice and this permission notice shall be 116 | included in all copies or substantial portions of the Software. 117 | 118 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 119 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 120 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 121 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 122 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 123 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 124 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 125 | OTHER DEALINGS IN THE SOFTWARE. 126 | 127 | =============================================== 128 | =============================================== 129 | =============================================== 130 | Medoo database framework 131 | https://medoo.in 132 | * Version 1.6 133 | * 134 | * Copyright 2018, Angel Lai 135 | * Released under the MIT license 136 | * 137 | 138 | MIT License 139 | 140 | Copyright (c) 2018 Angel Lai 141 | 142 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 143 | 144 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 145 | 146 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KongMap # 2 | Kongmap is a free visualization tool which allows you to view and declaratively edit configurations of 3 | your Kong API Gateway Clusters, including Routes, Services, and Plugins/Policies. The tool is 4 | available for installation on Docker and Kubernetes only at this time. 5 | 6 | [View list of latest changes/updates here.](https://github.com/yesinteractive/kong-map/blob/main/CHANGELOG.md) 7 | 8 | ![GitHub](https://img.shields.io/apm/l/vim-mode?style=for-the-badge) 9 | [![Docker Pulls](https://img.shields.io/docker/pulls/yesinteractive/kongmap?style=for-the-badge)](https://hub.docker.com/r/yesinteractive/kongmap) 10 | [![Version](https://img.shields.io/github/v/release/yesinteractive/kong-map?style=for-the-badge)](https://github.com/yesinteractive/kong-map/blob/main/CHANGELOG.md) 11 | 12 | - [Features](#Features) 13 | - [Cluster View](#Cluster-View) 14 | - [Endpoint Analyzer](#Endpoint-Analyzer) 15 | - [Declarative Configuration Viewer/Editor](#Declarative-Configuration-Viewer-Editor) 16 | - [Compatibility](#Compatibility) 17 | - [Docker Installation](#Docker-Installation) 18 | - [Questions and Feedback](#Feedback-and-Issues) 19 | 20 | ## Features 21 | 22 | #### Cluster View 23 | Allows an admin to view a dynamic map of their Kong API Gateway clusters and visually see relationships between 24 | Workspaces (for Kong Enterprise), Services, Routes (Endpoints), and Plugins (Policies). Cluster view can also 25 | be used to see configuration of the proxy plane of your Kong for Kubernetes Ingress Controller. Clicking on any 26 | entity displays details of the entity and related links. Plugins can be toggled from view and map is searchable 27 | (search by entity name, Kong tag, workspace, url, or any other details related to a Kong entity.) 28 | 29 | If editing is enabled, any Kong entity can be edited from the Cluster View map. Clicking on the edit button from 30 | any entity will send user directly to that entity in the declarative editor. 31 | 32 | 33 | ![alt text](https://github.com/yesinteractive/kong-map/blob/main/screenshots/kongmap-home.png?raw=true "kongmap") 34 | 35 | #### Endpoint Analyzer 36 | View details of an API Endpoint (Route). The analyzer shows the Service attached to the endpoint/route as well as provides 37 | a breakdown of all plugins/policies in order of execution attached to the route/endpoint. For Kong Enterprise users, 38 | all entities can be viewed directly via a link to Kong Manager. 39 | 40 | If editing is enabled, any Kong entity can be edited from the Endpoint Analyzer map. Clicking on the edit button from 41 | any entity will send user directly to that entity in the declarative editor. 42 | 43 | ![alt text](https://github.com/yesinteractive/kong-map/blob/main/screenshots/kongmap-endpoint.png?raw=true "kongmap") 44 | 45 | 46 | #### Declarative Configuration Viewer Editor 47 | KongMap is deployed with a browser based implementation of Kong's CLI tool, decK. Here you can view, edit, and export 48 | Kong declarative configurations for your open source and Enterprise clusters via YAML. Configurations can easily 49 | be copied and pasted from one Kong cluster to another or between workspaces. Declarative 50 | configuration editing can be disabled by KongMap configuration, or managed via RBAC permissions if using Kong Enterprise. 51 | 52 | The Viewer/Editor can be invoked from the Cluster Map view by clicking on on any Kong entity, and from 53 | any element from the Endpoint Analyzer. Kong entity ID's can be toggled in and out of view with the viewer/editor. 54 | 55 | ![alt text](https://github.com/yesinteractive/kong-map/blob/main/screenshots/kongmap-deck.png?raw=true "kongmap") 56 | 57 | ## Compatibility 58 | KongMap supports both Kong Open Source and Kong Enterprise Clusters greater than version 1.3 and supports both DB and Non-DB (dbless) Kong configurations. 59 | KongMap also supports Kong for Kubernetes Ingress Controller versions greater than 0.5 (In Kong for Kubernetes, 60 | the Ingress Controller's proxy container must have its Admin API exposed in some fashion.) 61 | 62 | KongMap uses various public CDN's for various UI elements such as Bootstrap, jQuery, etc. so KongMap will not display 63 | correctly in a browser on a closed network without Internet access. 64 | 65 | ## Docker Installation 66 | 67 | Docker image is Alpine 3.11 based running PHP 7.3 on Apache. The container exposes both ports 8100 an 8143 with a self signed certificated. 68 | 69 | Below are instructions using the `docker run` command. For an example using `docker-compose`, see the example in the [examples directory folder.](https://github.com/yesinteractive/kong-map/blob/main/examples) 70 | 71 | #### 1. Export Cluster Configurations to `KONG_CLUSTERS` Environment Variable 72 | 73 | The connections to your Kong clusters are defined via JSON. The below example illustrates adding two Kong clusters to KongMap: 74 | 75 | ```json 76 | { 77 | "my enterprise cluster": { 78 | "kong_admin_api_url": "http://kongapi_url:8001", 79 | "kong_edit_config": "true", 80 | "kong_ent": "true", 81 | "kong_ent_token": "admin", 82 | "kong_ent_token_name": "kong-admin-token", 83 | "kong_ent_manager_url": "http://kongmanager_url:8002" 84 | }, 85 | "my kong open source cluster": { 86 | "kong_admin_api_url": "http://kongapi_url:8001", 87 | "kong_edit_config": "true", 88 | "kong_ent": "false", 89 | "kong_ent_token": "null", 90 | "kong_ent_token_name": "null", 91 | "kong_ent_manager_url": "null" 92 | } 93 | } 94 | ``` 95 | Below is a definition of all variables in the KONG_CLUSTERS json config. All variables are required. 96 | 97 | | Parameter | Description | Required | 98 | |------------------------|-------------|-----------| 99 | | `kong_admin_api_url` | Full URL to Kong Admin API URL. Make sure there are no trailing slashes in the URL or KongMap will not work properly. Example: `http://kongadminapi:8001` | `yes` | 100 | | `kong_edit_config` | Boolean. Set to `true` to allow editing of Kong configs via KongMap. `false` will only allow readonly access to configs. | `yes` | 101 | | `kong_ent` | Boolean. Set `true` if you are connecting to a Kong Enterprise Cluster and to enable workspace support in KongMap. Only the default workspace will show if set to `false` and connected to a Kong Entperprise cluster. Otherwise set to `false` | `yes` | 102 | | `kong_ent_token` | The admin token for connecting to your Kong Enterprise Cluster Admin API. Set by RBAC configuration in Kong. Can be set to `null` if not needed. | `yes` | 103 | | `kong_ent_token_name` | The admin token header name for connecting to your Kong Enterprise Cluster Admin API. Typically is `kong-admin-token`. Can be set to `null` if not needed. | `yes` | 104 | | `kong_ent_manager_url` | Full URL to a Kong Manager if you wish to open entities in Kong Manager from KongMap. Can be set to `null` if not needed or if you do not want any Kong Manager buttons shown for the cluster. | `yes` | 105 | 106 | Export the config to a variable: 107 | 108 | ```shell 109 | export KONG_CLUSTERS='{ "my enterprise cluster": { "kong_admin_api_url": "http://kongapi_url:8001", "kong_edit_config": "true", "kong_ent": "true", "kong_ent_token": "admin", "kong_ent_token_name": "kong-admin-token", "kong_ent_manager_url": "http://kongmanager_url:8002" }}' 110 | ``` 111 | 112 | #### 2. Start Container 113 | 114 | Run the container with the following command. Set the ports to your preferred exposed ports. The example below exposes KongMap on ports 8100 and 8143. 115 | Notice the `KONGMAP_URL` variable. Set this variable to the KongMap URL that you will connect to KongMap in your browser. For example, if 116 | running locally and exposing KongMap on port 8100, set to `http://localhost:8100`. 117 | 118 | ``` 119 | $ docker run -d \ 120 | -e "KONGMAP_CLUSTERS_JSON=$KONG_CLUSTERS" \ 121 | -e "KONGMAP_URL=http://url_to_kongmap:8100" \ 122 | -p 8100:8100 \ 123 | -p 8143:8143 \ 124 | yesinteractive/kongmap 125 | ``` 126 | 127 | 128 | Full documentation available online here: [https://github.com/yesinteractive/kong-map/](https://github.com/yesinteractive/kong-map/) 129 | 130 | #### 3. Authentication 131 | 132 | If you want to enable authentication to KongMap's UI, it is recommended to run Kongmap behind your Kong Gateway and implement any authentication 133 | policies you feel is appropriate (OIDC, OAUTH2, Basic Auth, etc.) at the gateway. 134 | 135 | ## Feedback and Issues 136 | 137 | If you have questions, feedback or want to submit issues, please do so here: [https://github.com/yesinteractive/kong-map/issues](https://github.com/yesinteractive/kong-map/issues). 138 | -------------------------------------------------------------------------------- /config/fsl_config.php: -------------------------------------------------------------------------------- 1 | prepare($sql); 43 | # if ($stmt->execute()) 44 | # { 45 | # return $stmt->fetchAll(PDO::FETCH_ASSOC); 46 | # } 47 | # return false; 48 | # } 49 | # uncomment this section below if going to use a DB using PDO 50 | 51 | /* 52 | option('db_host','localhost'); 53 | option('db_name','testdb'); 54 | option('db_username','root'); 55 | option('db_password','test'); 56 | 57 | try{ 58 | $obj_db = new PDO('mysql:host='.option("db_host").';dbname='.option("db_name").';charset=utf8mb4', option("db_username"), option("db_password")); 59 | $obj_db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING ); 60 | $GLOBALS['obj_db'] = $obj_db; 61 | 62 | }catch(PDOException $e){ 63 | halt(SERVER_ERROR,"Connexion failed: ".$e); # raises an error / renders the error page and exit. 64 | 65 | } 66 | 67 | */ 68 | 69 | } 70 | 71 | ?> 72 | -------------------------------------------------------------------------------- /controllers/deck/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Table of Contents 2 | 3 | - [v1.7.0](#v170---20210520) 4 | - [v1.6.0](#v160---20210408) 5 | - [v1.5.1](#v151---20210323) 6 | - [v1.5.0](#v150---20210306) 7 | - [v1.4.0](#v140---20210201) 8 | - [v1.3.0](#v130---20210115) 9 | - [v1.2.4](#v124---20210106) 10 | - [v1.2.3](#v123---20201118) 11 | - [v1.2.2](#v122---20201019) 12 | - [v1.2.1](#v121---20200804) 13 | - [v1.2.0](#v120---20200804) 14 | - [v1.1.0](#v110---20200405) 15 | - [v1.0.3](#v103---20200314) 16 | - [v1.0.2](#v102---20200221) 17 | - [v1.0.1](#v101---20200214) 18 | - [v1.0.0](#v100---20200118) 19 | - [v0.7.2](#v072---20191229) 20 | - [v0.7.1](#v071---20191224) 21 | - [v0.7.0](#v070---20191207) 22 | - [v0.6.2](#v062---20191116) 23 | - [v0.6.1](#v061---20191108) 24 | - [v0.6.0](#v060---20191103) 25 | - [v0.5.2](#v052---20190915) 26 | - [v0.5.1](#v051---20190824) 27 | - [v0.5.0](#v050---20190818) 28 | - [v0.4.0](#v040---20190610) 29 | - [v0.3.0](#v030---20190514) 30 | - [v0.2.0](#v020---20190401) 31 | - [v0.1.0](#v010---20190112) 32 | 33 | ## [v1.7.0] - 2021/05/20 34 | 35 | ### Added 36 | 37 | - State files now support environment variable-based templating. decK can 38 | substitute the value of an environment variable into an object in the state 39 | file. This is useful for avoiding persistent cleartext storage of sensitive 40 | values and populating values that vary between similar configurations. 41 | [#286](https://github.com/Kong/deck/pull/286) 42 | - Sort state file objects by name, to ease comparing state files from 43 | similarly-configured instances that do not share object IDs. 44 | [#327](https://github.com/Kong/deck/pull/327) 45 | - Added a default timeout to HTTP requests. 46 | [37eeec8](https://github.com/Kong/deck/commit/37eeec8606583d2ecfacb3265f7ff15921f0ab8d) 47 | - Implemented `convert` command for converting state files between Kong Gateway 48 | and Konnect configuration formats. This is aimed to solving migration problem between 49 | on-premise Kong clusters and Konnect SaaS. 50 | [#330](https://github.com/Kong/deck/pull/330) 51 | - Add `--konnect-addr` flag to set Konnect address. This can be used to target Konnect 52 | data-centers in geographical regions other than the US. 53 | [#374](https://github.com/Kong/deck/pull/374) 54 | 55 | ### Fixed 56 | 57 | - Fixed duplicate error message prints. 58 | [#317](https://github.com/Kong/deck/pull/317) 59 | - Handle mtls-auth credential API behavior when Kong Enterprise is running in 60 | free mode. decK no longer treats the free mode mtls-auth behavior as a fatal 61 | error. 62 | [#321](https://github.com/Kong/deck/pull/321) 63 | - `--select-tag` tags are now applied to credentials. 64 | [#282](https://github.com/Kong/deck/pull/282) 65 | - Fix empty Service Package descriptions not syncing correctly. 66 | [#347](https://github.com/Kong/deck/pull/347) 67 | - Updating certificate fields no longer deletes SNI associations. 68 | [#386](https://github.com/Kong/deck/pull/386) 69 | 70 | ### Misc 71 | 72 | - Refactored utility functionality to take advantage of new features in 73 | go-kong. 74 | - Added reworked usage analytics. 75 | [#379](https://github.com/Kong/deck/pull/379) 76 | 77 | ## [v1.6.0] - 2021/04/08 78 | 79 | ### Added 80 | 81 | - decK now prompts by default before overwriting existing state files when 82 | dumping config. Including `--yes` in args assumes yes and overwrites state 83 | files without prompting. 84 | [#285](https://github.com/Kong/deck/pull/285) 85 | 86 | ### Misc 87 | 88 | - Removed analytics. 89 | [#301](https://github.com/Kong/deck/pull/301) 90 | 91 | ### Breaking changes 92 | 93 | - Changed `github.com/blang/semver` module to `github.com/blang/semver/v4`. If 94 | you use decK's `file` package in other applications, you will also need to 95 | update the semver module used in your application. 96 | [#303](https://github.com/Kong/deck/pull/303) 97 | 98 | ## [v1.5.1] - 2021/03/23 99 | 100 | ### Fixed 101 | 102 | - Targets with identical IP and port values no longer conflict when created for 103 | different upstreams. 104 | [#280](https://github.com/Kong/deck/pull/280) 105 | - Fixed issue where Konnect flag defaults overwrote non-Konnect flag defaults, 106 | which broke the `--all-workspaces` flag. 107 | [#290](https://github.com/Kong/deck/pull/290) 108 | - Diff output no longer prints resource timestamp information. 109 | [#283](https://github.com/Kong/deck/pull/283) 110 | - Tracebacks no longer include unwanted information specific to the build 111 | environment. 112 | [#284](https://github.com/Kong/deck/pull/284) 113 | 114 | ## [v1.5.0] - 2021/03/06 115 | 116 | ### Added 117 | 118 | - decK now supports Kong Konnect. Configuration for Kong Konnect can be exported, 119 | diffed and synced using decK. A new command `konnect` has been introduced for 120 | this purpose, which has 4 sub-commands: `ping`, `dump`, `diff`, and `sync`. 121 | This feature in decK is currently in `alpha` state, which means there can be 122 | breaking changes to these commands in future releases. 123 | - decK now supports two new Kong Enterprise resources: RBAC role and RBAC 124 | endpoint-permission. Special thanks to [@tjrivera](https://github.com/tjrivera) 125 | for this contribution. A new flag `--rbac-resources-only` has been introduced 126 | to manage RBAC-only configuration via decK. 127 | [#276](https://github.com/Kong/deck/pull/276) 128 | - Certificates and Kong Services can now be managed separately. A check for 129 | existence of Certificate has been relaxed to make this possible. 130 | [#269](https://github.com/Kong/deck/pull/269) 131 | 132 | ## [v1.4.0] - 2021/02/01 133 | 134 | ### Added 135 | 136 | - deck now handles the `request_buffering` and `response_buffering` options for `Route` 137 | [#261](https://github.com/Kong/deck/pull/261) 138 | 139 | ### Fixes 140 | 141 | - Updated brew syntax 142 | [#252](https://github.com/Kong/deck/pull/252) 143 | - Fixed YAML/JSON file detection logic 144 | [#255](https://github.com/Kong/deck/pull/255) 145 | 146 | ## [v1.3.0] - 2021/01/15 147 | 148 | ### Added 149 | 150 | - decK will now retry sync operations that encounter a 500 error several times 151 | before failing completely. 152 | [#226](https://github.com/Kong/deck/pull/226) 153 | 154 | ### Fixed 155 | 156 | - Fixed regression that broke workspace creation. 157 | [#252](https://github.com/Kong/deck/pull/252) 158 | - Analytics failures no longer delay execution. 159 | [#254](https://github.com/Kong/deck/pull/254) 160 | 161 | ## [v1.2.4] - 2021/01/06 162 | 163 | ### Fixed 164 | 165 | - Fixed a bug that disabled verbose output. 166 | [#243](https://github.com/Kong/deck/pull/243) 167 | - decK no longer considers tag order significant. This avoids unnecessary 168 | resource updates for Cassandra-backed clusters. 169 | [#240](https://github.com/Kong/deck/pull/240) 170 | 171 | ## [v1.2.3] - 2020/11/18 172 | 173 | ### Fixed 174 | 175 | - Sync operations now handle plugins with array configuration correctly. 176 | [#229](https://github.com/Kong/deck/pull/229) 177 | - Removed unecessary permissions requirement for checking workspace existence. 178 | [#225](https://github.com/Kong/deck/pull/225) 179 | 180 | ## [v1.2.2] - 2020/10/19 181 | 182 | ### Added 183 | 184 | - decK now prints a change summary even if it encountered an error. 185 | [#197](https://github.com/hbagdi/deck/pull/197) 186 | - decK now prints the ID of entities that it could not successfully sync. 187 | [#199](https://github.com/hbagdi/deck/pull/199) 188 | - Issues sending analytics will now emit a panic. 189 | [#200](https://github.com/hbagdi/deck/pull/200) 190 | - decK now creates the workspace specified with `--workspace` if it is not 191 | already present. 192 | [#201](https://github.com/hbagdi/deck/pull/201) 193 | - decK prints descriptive information about duplicated entities. 194 | [#204](https://github.com/hbagdi/deck/pull/204) 195 | 196 | ### Fixed 197 | 198 | - Resolved a concurrency bug during syncs. 199 | [#202](https://github.com/hbagdi/deck/pull/202) 200 | 201 | ## [v1.2.1] - 2020/08/04 202 | 203 | ### Summary 204 | 205 | decK has move under Kong's umbrella. 206 | Due to this change, the package path has changed from `github.com/hbagdi/deck` 207 | to `github.com/kong/deck`. 208 | This release contains the updated `go.mod` over v1.2.0. There are no 209 | other changes introduced in this release. 210 | 211 | ## [v1.2.0] - 2020/08/04 212 | 213 | ### Added 214 | 215 | - decK is now compatible with Kong 2.1: 216 | - New Admin API properties for entities are added. 217 | - Ordering of operations has changed to incorporate for new foreign-relations 218 | [#192](https://github.com/hbagdi/deck/pull/192) 219 | - New flag `--db-update-propagation-delay` to add an artifical delay 220 | between Admin API calls. This is introduced for better compatibility with 221 | Cassandra backed installations of Kong. 222 | [#160](https://github.com/hbagdi/deck/pull/160) 223 | [#154](https://github.com/hbagdi/deck/pull/154) 224 | - decK now errors out if there are invalid positional arguments supplied to 225 | any command. 226 | - Stricter validation of state files. 227 | [#162](https://github.com/hbagdi/deck/pull/162) 228 | - ID property of CACertificate is always exported. 229 | [#193](https://github.com/hbagdi/deck/pull/193) 230 | 231 | ### Fixed 232 | 233 | - Ignore error for missing `.deck` config file 234 | [#168](https://github.com/hbagdi/deck/pull/168) 235 | - Correctly populate port in Service's URL (a sugar attribute) 236 | [#166](https://github.com/hbagdi/deck/pull/166) 237 | - Correct the help text for `--tls-server-name` flag 238 | [#170](https://github.com/hbagdi/deck/pull/170) 239 | - Better sanitization of `--kong-addr` input 240 | [#171](https://github.com/hbagdi/deck/pull/171) 241 | - Fix typos in the output of `--help` 242 | [#174](https://github.com/hbagdi/deck/pull/174) 243 | - Improve language of warning message for basic-auth credentials 244 | [#145](https://github.com/hbagdi/deck/pull/145) 245 | - Deduplicate `select_tags` input 246 | [#183](https://github.com/hbagdi/deck/pull/183) 247 | 248 | 249 | ### Enterprise-only 250 | 251 | - Added support for managing `mtls-auth` credentials. 252 | [#175](https://github.com/hbagdi/deck/pull/175) 253 | - decK now automatically creates a workspace if one does not already exist 254 | during a `sync` operation. 255 | [#187](https://github.com/hbagdi/deck/pull/187) 256 | - Added `--workspace` flag to `ping` command. This can be used to verify 257 | connectivity with Kong Enterprise when running as an RBAC role with lower 258 | priviliges. 259 | - New `--workspace` flag for `diff` and `sync` command to provide workspace 260 | via the CLI instead of state file. Workspace defined in state file will be 261 | overriden if this flag is provided. 262 | - New `--skip-workspace-crud` flag to skip any workspace related operations. 263 | This flag can be used when running as as an RBAC role with lower priviliges. 264 | The content can be synced to specific workspaces but decK will not attempt 265 | to create or verify existence of a workspace. 266 | [#157](https://github.com/hbagdi/deck/pull/157) 267 | - Additional checks for existence of workspace before performing dump or reset 268 | [#167](https://github.com/hbagdi/deck/pull/167) 269 | - Improve end-user error message when workspace doesn't exist 270 | 271 | #### Misc 272 | 273 | - CI changed from Travis to Github Actions 274 | - Improved code quality with addition of golangci-lint 275 | - Default branch for the project has been changed from `master` to `main` 276 | 277 | ## [v1.1.0] - 2020/04/05 278 | 279 | ### Added 280 | 281 | - Added support for multiple files or directories to `-s/--state` 282 | flag. Use `-s` multiple times or specify multiple files/directories using 283 | a comma separated list. 284 | [#137](https://github.com/hbagdi/deck/pull/137) 285 | - **Performance** 286 | decK should be much faster than before. Requests to Kong are 287 | now concurrent. `dump`, `sync`, `diff` and `reset` 288 | commands will be faster than before, by at least 2x. 289 | - SNI entity in Kong is not supported natively supported 290 | [#139](https://github.com/hbagdi/deck/pull/139). Most users will not observe 291 | any changes. `id` and `tags` are now supported for the SNI entity in Kong. 292 | 293 | ### Under the hood 294 | 295 | - Go has been upgraded to 1.14.1 296 | - Alpine base image for Docker has been upgraded to 3.11 297 | - Multiple other dependencies have also been upgraded, but these have no 298 | user-visible changes. 299 | 300 | ### Fixed 301 | 302 | - Default values for `retries` in Service entity and 303 | `HTTPSVerifyCertificate` in Upstream entity have been removed. 304 | These values can be set to `0` and `false` respectively now. 305 | [#134](https://github.com/hbagdi/deck/issues/134) 306 | 307 | ## [v1.0.3] - 2020/03/14 308 | 309 | ### Fixed 310 | 311 | - Fix certificate diff for certificates with no associated snis 312 | [#131](https://github.com/hbagdi/deck/issues/131) 313 | 314 | ## [v1.0.2] - 2020/02/21 315 | 316 | ### Fixed 317 | 318 | - Fix broken `ca_certificate` entity support 319 | [#127](https://github.com/hbagdi/deck/pull/127) 320 | 321 | ## [v1.0.1] - 2020/02/14 322 | 323 | ### Added 324 | 325 | - decK now supports the `url` sugar property on Service entity. 326 | [#123](https://github.com/hbagdi/deck/issues/123) 327 | 328 | ## [v1.0.0] - 2020/01/18 329 | 330 | ### Fixed 331 | 332 | - decK doesn't error out if bundled plugins in Kong are disabled 333 | [#121](https://github.com/hbagdi/deck/pull/121) 334 | - Consumer-specific plugins are excluded when `--skip-consumers` is used 335 | [#119](https://github.com/hbagdi/deck/issues/119) 336 | 337 | ### Internal 338 | 339 | - `go-kong` has been upgraded to v0.11.0, which brings in support for 340 | Kong 2.0. 341 | - All other dependencies have also been upgraded, but these have no 342 | user-visible changes. 343 | [b603f9](https://github.com/hbagdi/deck/commit/b603f9) 344 | 345 | ## [v0.7.2] - 2019/12/29 346 | 347 | ### Fixed 348 | 349 | - Kong's version is correctly parsed; v0.7.1 is unusable because 350 | of this bug. 351 | [#117](https://github.com/hbagdi/deck/issues/117) 352 | 353 | ## [v0.7.1] - 2019/12/24 354 | 355 | ### Fixed 356 | 357 | - Backward compatibility for credentials; tags are no longer injected into 358 | credentials for Kong versions below 1.4 359 | [#114](https://github.com/hbagdi/deck/issues/114) 360 | 361 | ## [v0.7.0] - 2019/12/07 362 | 363 | ### Breaking changes 364 | 365 | - `sync` command now shows the progress of the sync. Previously, the command 366 | did not output anything but errors. 367 | 368 | ### Added 369 | 370 | - Configuration of multiple plugin instances can now be de-duplicated using 371 | `_plugin_configs` field in the state file. 372 | [#93](https://github.com/hbagdi/deck/issues/93) 373 | - A summary is now presented at the end of a `diff` or `sync` 374 | operation showing the count of resources created/updated/deleted. 375 | [#101](https://github.com/hbagdi/deck/issues/101) 376 | - `sync` command now shows the progress of the sync as the sync takes place, 377 | making it easier to track progress in large environments. 378 | [#100](https://github.com/hbagdi/deck/issues/100) 379 | - `--non-zero-exit-code` flag hsa been added to `diff` command. Using 380 | this flag causes decK to exit with a non-zero exit code if a diff is 381 | detected, making it easier to script decK in CI pipelines. 382 | [#98](https://github.com/hbagdi/deck/issues/98) 383 | - A new docs website has been setup for the project: 384 | [https://deck.yolo42.com](https://deck.yolo42.com) 385 | 386 | ## [v0.6.2] - 2019/11/16 387 | 388 | ### Fixed 389 | 390 | - Service-less routes are correctly processed 391 | [#103](https://github.com/hbagdi/deck/issues/103) 392 | - Plugins for routes are correctly processed 393 | [#104](https://github.com/hbagdi/deck/issues/104) 394 | 395 | ## [v0.6.1] - 2019/11/08 396 | 397 | ### Fixed 398 | 399 | - Check for workspace makes call the right endpoint 400 | [#94](https://github.com/hbagdi/deck/issues/94) 401 | - Error checking is performed correctly when ensuring existence of a workspace 402 | [#95](https://github.com/hbagdi/deck/issues/95) 403 | - Multiple upstream definitions are read correctly and synced up 404 | [#96](https://github.com/hbagdi/deck/issues/96) 405 | 406 | ## [v0.6.0] - 2019/11/03 407 | 408 | ### Breaking changes 409 | 410 | - `ID` field is required for `Certificate` entity. Previous state files will 411 | break if `ID` is not present on this entity. You can use `dump` command 412 | to generate new state files which includes the `ID` field. 413 | - SNIs are exported under the `name` key under Certificate entity to match 414 | Kong's declarative configuration format. 415 | 416 | ### Added 417 | 418 | - Kong's configuration can now be synced/diffed/dumped using JSON format, 419 | in addition to the existing YAML format. Use the `--format` flag to specify 420 | the format. 421 | [#35](https://github.com/hbagdi/deck/issues/35) 422 | - Plugins associated with multiple entities e.g. a plugin for a combination of 423 | route and a consumer in Kong are now supported. 424 | [#13](https://github.com/hbagdi/deck/issues/13) 425 | - JSON-schema based validation is now performed on the input file(s) for every 426 | command. 427 | - New `validate` command has been added to validate an existing state file. 428 | This performs a JSON-schema based sanity check on the file along-with foreign 429 | reference checks to check for dangling pointers. 430 | - Service-less routes are now supported by decK. 431 | - `name` is no longer a required field for routes and services entities 432 | in Kong. If a `name` is not present, decK exports the entity with it's `ID`. 433 | - Client-certificates on Service entity are now a supported. 434 | - Credential entities like key-auth, basic-auth now support tagging. 435 | - `--parallelism` flag has been added to `sync` and `diff` commands to control 436 | the number of concurrenty request to Kong's Admin API. 437 | [#85](https://github.com/hbagdi/deck/issues/85) 438 | - `diff` and `sync` show a descriptive error when a workspace doesn't exist 439 | for Kong Enterprise. 440 | [102ed5dd](https://github.com/hbagdi/deck/commit/102ed5dd6f8ef) 441 | - `--select-tag` flag has been added to `diff` and `sync` command for use-cases 442 | where the tags are not part of the state file. It is not recommended to 443 | use these flags unless you know what you are doing. 444 | [#81](https://github.com/hbagdi/deck/issues/81) 445 | - ID for any entity can now be specified. decK previously ignored the ID for 446 | any entity if one was specified. Entities can also be exported with the `ID` 447 | field set using `--with-id` flag on the `dump` command. 448 | [#29](https://github.com/hbagdi/deck/issues/29) 449 | 450 | ### Fixed 451 | 452 | - decK runs as non-root user in the Docker image. 453 | [#82](https://github.com/hbagdi/deck/issues/82) 454 | - SNIs are now exported same as Kong's format i.e. they are exported under a 455 | `name` key under the certificates entity. 456 | [#76](https://github.com/hbagdi/deck/issues/76) 457 | - Errors are made more descriptive in few commands. 458 | - decK's binary inside the Docker image now contains versioning information. 459 | [#38](https://github.com/hbagdi/deck/issues/38) 460 | 461 | 462 | ### Internal 463 | 464 | - Go has been bumped up to `1.13.4`. 465 | - `go-kong` has been bumped up to `v0.10.0`. 466 | - Reduced memory allocation, which should result in less GC pressure. 467 | 468 | ## [v0.5.2] - 2019/09/15 469 | 470 | ### Added 471 | 472 | - `-w/--workspace` flag has been added to the `reset` command to reset a 473 | specific workspace in Kong Enterprise. 474 | [#74](https://github.com/hbagdi/deck/issues/74) 475 | - `--all-workspaces` flag has been added to the `reset` command to reset 476 | all workspaces in Kong Enterprise. 477 | [#74](https://github.com/hbagdi/deck/issues/74) 478 | - A warning is logged when basic-auth credentials are being synced. 479 | [#49](https://github.com/hbagdi/deck/issues/49) 480 | 481 | ### Fixed 482 | 483 | - Kong Enterprise Developer Portal exposes the credentials (basic/key) of 484 | Developers on the Admin API, but doesn't expose the consumers causing 485 | issues during export. decK now ignores these credentials in Kong Enterprise. 486 | [#75](https://github.com/hbagdi/deck/issues/75) 487 | 488 | ### Internal 489 | 490 | - Go version has been bumped to 1.13. 491 | 492 | ## [v0.5.1] - 2019/08/24 493 | 494 | ### Added 495 | 496 | - `oauth2` credentials associated with consumers are now supported. 497 | [#67](https://github.com/hbagdi/deck/pull/67) 498 | 499 | ### Fixed 500 | 501 | - The same target can be associated with multiple upstreams. 502 | [#57](https://github.com/hbagdi/deck/issues/57) 503 | - Fix compatibility with Kong < 1.3. 504 | [#59](https://github.com/hbagdi/deck/issues/59) 505 | - Ignore credentials for consumers which are not in the sub-set of 506 | the configuration being synced. 507 | [#65](https://github.com/hbagdi/deck/issues/65) 508 | 509 | ## [v0.5.0] - 2019/08/18 510 | 511 | ### Summary 512 | 513 | This release brings the following features: 514 | - Consumer credentials are now supported 515 | - Support for Kong 1.3 516 | - Kong Enterprise workspace support 517 | - Reading configuration from multiple files in a directories 518 | 519 | ### Breaking changes 520 | 521 | No breaking changes have been introduced in this release. 522 | 523 | ### Added 524 | 525 | - **Consumer credentials** 526 | The following entities associate with a consumer in Kong are now supported [#12](https://github.com/hbagdi/deck/issues/12): 527 | - `key-auth` 528 | - `basic-auth` 529 | - `hmac-auth` 530 | - `jwt` 531 | - `acl` 532 | 533 | - decK's exported YAML is now compatible with Kong's declarative config 534 | file. 535 | - **Homebrew support** 536 | decK can now be installed using Homebrew on macOS: 537 | ``` 538 | brew tap hbagdi/deck 539 | brew install deck 540 | ``` 541 | - **Multiple state files** 542 | decK can now read the configuration of Kong from multiple YAML files in a directory. You can split your configuration 543 | into files in any way you would like. 544 | [#22](https://github.com/hbagdi/deck/issues/22) 545 | - Upcoming Kong 1.3 is now supported. 546 | [#36](https://github.com/hbagdi/deck/issues/36) 547 | - **Kong Enterprise only features:** 548 | Workspaces are now natively supported in decK 549 | - `-w/--workspace` flag can be specified in the `dump` command to 550 | export configuration of a single workspace. 551 | - `--all-workspaces` flag in `dump` command will export all workspaces 552 | in Kong Enteprise. Each workspace lives in a separate state file. 553 | - `diff` and `sync` command now support workspaces via the `_workspace` 554 | attribute in the state file. 555 | 556 | ### Fixed 557 | 558 | - decK now supports TCP services in Kong. 559 | [#44](https://github.com/hbagdi/deck/issues/44) 560 | - Add missing `interval` field in Upstream entity's 561 | unhealthy active healthchecks 562 | [#45](https://github.com/hbagdi/deck/pull/45) 563 | - Docker image now contains only the binary and not the entire source code. 564 | [#34](https://github.com/hbagdi/deck/pull/34) 565 | Thanks to [David Cruz](https://github.com/davidcv5) for the contribution. 566 | 567 | ## [v0.4.0] - 2019/06/10 568 | 569 | ### Summary 570 | 571 | This release introduces support for Kong 1.2.x. 572 | 573 | ### Breaking changes 574 | 575 | - `strip_path` attribute of Route can now be set to false. The default value 576 | is now false, which was true previously. 577 | [#18](https://github.com/hbagdi/deck/issues/18) 578 | 579 | ### Added 580 | 581 | - `https_redirect_status_code` attribute of Route in Kong can be set, 582 | and defaults to `426`. 583 | 584 | ## [v0.3.0] - 2019/05/14 585 | 586 | ### Breaking changes 587 | 588 | No breaking changes have been introduced in this release. 589 | 590 | ### Added 591 | 592 | - **Tag-based distributed configuration management** 593 | Only a subset of Kong entities sharing a (set of) tag can now be exported, 594 | deleted, diffed or synced. 595 | decK can now manage your Kong's configuration in a distributed manner, 596 | whereby you can split Kong's configuration by team and each team can manage 597 | it's own configuration. Use `select-tag` feature in all the commands and 598 | config file for this purpose. 599 | [#17](https://github.com/hbagdi/deck/pull/17) 600 | - **Read/write state from stdout/stdin** 601 | Config file can now be read in from standard-input and written out to 602 | standard-output. 603 | [#10](https://github.com/hbagdi/deck/pull/10), 604 | [#11](https://github.com/hbagdi/deck/pull/11) 605 | Thanks to [@matthewbednarski](https://github.com/matthewbednarski) for the contribution. 606 | - **Automated defaults** 607 | No need to specify default values for all core Kong entities, 608 | further simplifying your Kong's configuration. 609 | Default values for plugin configuration still need to be defined, this is on 610 | the roadmap. 611 | [b448d4f](https://github.com/hbagdi/deck/commit/b448d4f) 612 | - Add support for new properties in Upstream entity in Kong. 613 | [080200d](https://github.com/hbagdi/deck/commit/080200d) 614 | - Empty plugins and other Kong entities are not populated in the config file 615 | as empty arrays to keep the file concise and clean. 616 | [ae38f1b](https://github.com/hbagdi/deck/commit/ae38f1b) 617 | - Docker image is now available via Docker Hub. 618 | You can use `docker pull hbagdi/deck` to pull down decK in a Docker image. 619 | 620 | ### Fixed 621 | 622 | - Empty arrays in plugin configs are not treated as nil anymore. 623 | [#9](https://github.com/hbagdi/deck/pull/9) 624 | - Correctly sync plugins which are out of sync. Protocols field 625 | in plugins can be confused with protocols field in routes in Kong 626 | [#6](https://github.com/hbagdi/deck/pull/6) 627 | Thanks to [@davidcv5](https://github.com/davidcv5) for the contribution. 628 | - Throw an error if an object is not marshalled into YAML correctly. 629 | - Correctly create service-level plugins for Kong >= 1.1 630 | [#16](https://github.com/hbagdi/deck/pull/16) 631 | 632 | ### Misc 633 | 634 | - `go-kong` has been bumped up to v0.4.1. 635 | 636 | ## [v0.2.0] - 2019/04/01 637 | 638 | ### Breaking changes 639 | 640 | No breaking changes have been introduced in this release. 641 | 642 | ### Added 643 | 644 | - **Consumers and consumer-level plugins** can now be exported from Kong and 645 | synced to Kong. 646 | - `--skip-consumers` flag has been introduced to various sub-commands to skip 647 | management of consumers in environments where they are created dynamically.` 648 | - **Authentication support**: custom HTTP Headers (key:value) can be injected 649 | into requests that decK makes to Kong's Admin API using the `--headers` 650 | CLI flag. 651 | [#1](https://github.com/hbagdi/deck/pull/1) 652 | Thanks to [@davidcv5](https://github.com/davidcv5) for the contribution. 653 | 654 | ### Fixed 655 | 656 | - Infinite loop in pagination for exporting entities in Kong 657 | [#2](https://github.com/hbagdi/deck/pull/2) 658 | Thanks to [@lmika](https://github.com/lmika) for the contribution. 659 | - Plugins are updated using PUT requests instead of PATCH to 660 | avoid any schema violations. 661 | 662 | ## [v0.1.0] - 2019/01/12 663 | 664 | ### Summary 665 | 666 | Debut release of decK 667 | 668 | [v1.7.0]: https://github.com/kong/deck/compare/v1.6.0...v1.7.0 669 | [v1.6.0]: https://github.com/kong/deck/compare/v1.5.1...v1.6.0 670 | [v1.5.1]: https://github.com/kong/deck/compare/v1.5.0...v1.5.1 671 | [v1.5.0]: https://github.com/kong/deck/compare/v1.4.0...v1.5.0 672 | [v1.4.0]: https://github.com/kong/deck/compare/v1.3.0...v1.4.0 673 | [v1.3.0]: https://github.com/kong/deck/compare/v1.2.4...v1.3.0 674 | [v1.2.4]: https://github.com/kong/deck/compare/v1.2.3...v1.2.4 675 | [v1.2.3]: https://github.com/kong/deck/compare/v1.2.2...v1.2.3 676 | [v1.2.2]: https://github.com/kong/deck/compare/v1.2.1...v1.2.2 677 | [v1.2.1]: https://github.com/hbagdi/deck/compare/v1.2.0...v1.2.1 678 | [v1.2.0]: https://github.com/hbagdi/deck/compare/v1.1.0...v1.2.0 679 | [v1.1.0]: https://github.com/hbagdi/deck/compare/v1.0.3...v1.1.0 680 | [v1.0.3]: https://github.com/hbagdi/deck/compare/v1.0.2...v1.0.3 681 | [v1.0.2]: https://github.com/hbagdi/deck/compare/v1.0.1...v1.0.2 682 | [v1.0.1]: https://github.com/hbagdi/deck/compare/v1.0.0...v1.0.1 683 | [v1.0.0]: https://github.com/hbagdi/deck/compare/v0.7.2...v1.0.0 684 | [v0.7.2]: https://github.com/hbagdi/deck/compare/v0.7.1...v0.7.2 685 | [v0.7.1]: https://github.com/hbagdi/deck/compare/v0.7.0...v0.7.1 686 | [v0.7.0]: https://github.com/hbagdi/deck/compare/v0.6.2...v0.7.0 687 | [v0.6.2]: https://github.com/hbagdi/deck/compare/v0.6.1...v0.6.2 688 | [v0.6.1]: https://github.com/hbagdi/deck/compare/v0.6.0...v0.6.1 689 | [v0.6.0]: https://github.com/hbagdi/deck/compare/v0.5.2...v0.6.0 690 | [v0.5.2]: https://github.com/hbagdi/deck/compare/v0.5.1...v0.5.2 691 | [v0.5.1]: https://github.com/hbagdi/deck/compare/v0.5.0...v0.5.1 692 | [v0.5.0]: https://github.com/hbagdi/deck/compare/v0.4.0...v0.5.0 693 | [v0.4.0]: https://github.com/hbagdi/deck/compare/v0.3.0...v0.4.0 694 | [v0.3.0]: https://github.com/hbagdi/deck/compare/v0.2.0...v0.3.0 695 | [v0.2.0]: https://github.com/hbagdi/deck/compare/v0.1.0...v0.2.0 696 | [v0.1.0]: https://github.com/hbagdi/deck/compare/0c7e839...v0.1.0 697 | -------------------------------------------------------------------------------- /controllers/deck/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2018-2020 Harry Bagdi 191 | Copyright 2020-2020 Kong Inc. 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | -------------------------------------------------------------------------------- /controllers/deck/README.md: -------------------------------------------------------------------------------- 1 | # decK: Declarative configuration for Kong 2 | 3 | decK provides declarative configuration and drift detection for Kong. 4 | 5 | [![Build Status](https://github.com/kong/deck/workflows/CI%20Test/badge.svg)](https://github.com/kong/deck/actions?query=branch%3Amain+event%3Apush) 6 | [![codecov](https://codecov.io/gh/Kong/deck/branch/main/graph/badge.svg?token=m9WNK9rFEG)](https://codecov.io/gh/Kong/deck) 7 | [![Go Report Card](https://goreportcard.com/badge/github.com/kong/deck)](https://goreportcard.com/report/github.com/kong/deck) 8 | 9 | [![asciicast](https://asciinema.org/a/238318.svg)](https://asciinema.org/a/238318) 10 | 11 | ## Table of Content 12 | 13 | - [**Features**](#features) 14 | - [**Compatibility**](#compatibility) 15 | - [**Installation**](#installation) 16 | - [**Documentation**](#documentation) 17 | - [**Stale issue and pull request policy**](#stale-issue-and-pull-request-policy) 18 | - [**License**](#license) 19 | 20 | ## Features 21 | 22 | - **Export** 23 | Existing Kong configuration to a YAML configuration file 24 | This can be used to backup Kong's configuration. 25 | - **Import** 26 | Kong's database can be populated using the exported or a hand written config 27 | file. 28 | - **Diff and sync capabilities** 29 | decK can diff the configuration in the config file and 30 | the configuration in Kong's DB and then sync it as well. 31 | This can be used to detect config drifts or manual interventions. 32 | - **Reverse sync** 33 | decK supports a sync the other way as well, meaning if an 34 | entity is created in Kong and doesn't add it to the config file, 35 | decK will detect the change. 36 | - **Validation** 37 | decK can validate a YAML file that you backup or modify to catch errors 38 | early on. 39 | - **Reset** 40 | This can be used to drops all entities in Kong's DB. 41 | - **Parallel operations** 42 | All Admin API calls to Kong are executed in parallel using multiple 43 | threads to speed up the sync process. 44 | - **Authentication with Kong** 45 | Custom HTTP headers can be injected in requests to Kong's Admin API 46 | for authentication/authorization purposes. 47 | - **Manage Kong's config with multiple config file** 48 | Split your Kong's configuration into multiple logical files based on a shared 49 | set of tags amongst entities. 50 | - **Designed to automate configuration management** 51 | decK is designed to be part of your CI pipeline and can be used to not only 52 | push configuration to Kong but also detect drifts in configuration. 53 | 54 | ## Compatibility 55 | 56 | decK is compatible with Kong Gateway >= 1.x and Kong Enterprise >= 0.35. 57 | 58 | ## Installation 59 | 60 | ### macOS 61 | 62 | If you are on macOS, install decK using brew: 63 | 64 | ```shell 65 | $ brew tap kong/deck 66 | $ brew install deck 67 | ``` 68 | 69 | ### Linux 70 | 71 | If you are Linux, you can either use the Debian or RPM archive from 72 | the Github [release page](https://github.com/kong/deck/releases) 73 | or install by downloading the binary: 74 | 75 | ```shel 76 | $ curl -sL https://github.com/kong/deck/releases/download/v1.2.0/deck_1.2.0_linux_amd64.tar.gz -o deck.tar.gz 77 | $ tar -xf deck.tar.gz -C /tmp 78 | $ sudo cp /tmp/deck /usr/local/bin/ 79 | ``` 80 | 81 | ### Docker image 82 | 83 | Docker image is hosted on [Docker Hub](https://hub.docker.com/r/kong/deck). 84 | 85 | You can get the image with the command: 86 | 87 | ``` 88 | docker pull kong/deck 89 | ``` 90 | 91 | ## Documentation 92 | 93 | You can use `--help` flag once you've decK installed on your system 94 | to get help in the terminal itself. 95 | 96 | The project's documentation is hosted at 97 | [https://docs.konghq.com/deck/overview](https://docs.konghq.com/deck/overview). 98 | 99 | ## Changelog 100 | 101 | Changelog can be found in the [CHANGELOG.md](CHANGELOG.md) file. 102 | 103 | ## Stale issue and pull request policy 104 | 105 | To ensure our backlog is organized and up to date, we will close issues and 106 | pull requests that have been inactive awaiting a community response for over 2 107 | weeks. If you wish to reopen a closed issue or PR to continue work, please 108 | leave a comment asking a team member to do so. 109 | 110 | ## License 111 | 112 | decK is licensed with Apache License Version 2.0. 113 | Please read the [LICENSE](LICENSE) file for more details. 114 | -------------------------------------------------------------------------------- /controllers/deck/deck: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesinteractive/kong-map/a2847d32445e67cce3e05fd621f8c571fe56bdf1/controllers/deck/deck -------------------------------------------------------------------------------- /controllers/deck/kong.yaml: -------------------------------------------------------------------------------- 1 | Emptied -------------------------------------------------------------------------------- /examples/dadjokes.yaml: -------------------------------------------------------------------------------- 1 | # Example declarative configuration for Kong API Gateway. 2 | # Will create /jokes endpoint with a rate limit of 5 3 | # requests per minute. Dadjokes.online is being used 4 | # as the upstream searvice. 5 | _format_version: "1.1" 6 | services: 7 | - connect_timeout: 60000 8 | host: dadjokes.online 9 | name: dad_jokes 10 | path: /echo 11 | port: 80 12 | protocol: http 13 | read_timeout: 60000 14 | retries: 5 15 | write_timeout: 60000 16 | routes: 17 | - name: dad_jokes 18 | paths: 19 | - /jokes 20 | path_handling: v1 21 | preserve_host: false 22 | protocols: 23 | - http 24 | regex_priority: 0 25 | strip_path: true 26 | https_redirect_status_code: 426 27 | plugins: 28 | - name: rate-limiting 29 | config: 30 | day: null 31 | fault_tolerant: true 32 | header_name: null 33 | hide_client_headers: false 34 | hour: null 35 | limit_by: consumer 36 | minute: 5 37 | month: null 38 | policy: cluster 39 | redis_database: 0 40 | redis_host: null 41 | redis_password: null 42 | redis_port: 6379 43 | redis_timeout: 2000 44 | second: null 45 | year: null 46 | enabled: true 47 | protocols: 48 | - grpc 49 | - grpcs 50 | - http 51 | - https -------------------------------------------------------------------------------- /examples/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | # docker compose example 2 | # exposes kongmap on port 8100 3 | # must set KONGMAP_CLUSTERS_JSON either in file or as a variable with export command prior 4 | # usage example: docker-compose -f docker-compose.yaml up -d 5 | --- 6 | version: '3' 7 | services: 8 | kongmap: 9 | image: docker.io/yesinteractive/kongmap 10 | container_name: kongmap 11 | environment: 12 | - KONGMAP_CLUSTERS_JSON=$KONG_CLUSTERS 13 | - KONGMAP_URL=http://url_to_kongmap:8100 14 | restart: always 15 | ports: 16 | - "8100:80" 17 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | \n"; 70 | $output .= ""; 73 | 74 | */ 75 | 76 | return $output; 77 | } 78 | 79 | 80 | run(); 81 | 82 | ############################################################################## 83 | # Data Models 84 | ############################################################################## 85 | # 86 | 87 | ############################################################################## 88 | # layouts (views) and html templates 89 | ############################################################################## 90 | # Layouts are autoloaded from views directory or can be referended 91 | # as a function like below. 92 | 93 | function html_my_layout($vars){ extract($vars);?> 94 | 95 | 96 | 97 | 98 | Hello world! 99 | 100 | 101 | 102 | 105 |

Hello !

106 |

How are you ?

107 |
108 |

109 |

110 |

Your request for " . $errstr . " came up ghosts." ; 120 | return html($code); 121 | } 122 | 123 | 124 | 125 | ?> 126 | -------------------------------------------------------------------------------- /lib/3rd_party_helpers/Medoo.php: -------------------------------------------------------------------------------- 1 | type = strtolower($options[ 'database_type' ]); 50 | 51 | if ($this->type === 'mariadb') 52 | { 53 | $this->type = 'mysql'; 54 | } 55 | } 56 | 57 | if (isset($options[ 'prefix' ])) 58 | { 59 | $this->prefix = $options[ 'prefix' ]; 60 | } 61 | 62 | if (isset($options[ 'logging' ]) && is_bool($options[ 'logging' ])) 63 | { 64 | $this->logging = $options[ 'logging' ]; 65 | } 66 | 67 | $option = isset($options[ 'option' ]) ? $options[ 'option' ] : []; 68 | $commands = (isset($options[ 'command' ]) && is_array($options[ 'command' ])) ? $options[ 'command' ] : []; 69 | 70 | switch ($this->type) 71 | { 72 | case 'mysql': 73 | // Make MySQL using standard quoted identifier 74 | $commands[] = 'SET SQL_MODE=ANSI_QUOTES'; 75 | 76 | break; 77 | 78 | case 'mssql': 79 | // Keep MSSQL QUOTED_IDENTIFIER is ON for standard quoting 80 | $commands[] = 'SET QUOTED_IDENTIFIER ON'; 81 | 82 | // Make ANSI_NULLS is ON for NULL value 83 | $commands[] = 'SET ANSI_NULLS ON'; 84 | 85 | break; 86 | } 87 | 88 | if (isset($options[ 'pdo' ])) 89 | { 90 | if (!$options[ 'pdo' ] instanceof PDO) 91 | { 92 | throw new InvalidArgumentException('Invalid PDO object supplied'); 93 | } 94 | 95 | $this->pdo = $options[ 'pdo' ]; 96 | 97 | foreach ($commands as $value) 98 | { 99 | $this->pdo->exec($value); 100 | } 101 | 102 | return; 103 | } 104 | 105 | if (isset($options[ 'dsn' ])) 106 | { 107 | if (is_array($options[ 'dsn' ]) && isset($options[ 'dsn' ][ 'driver' ])) 108 | { 109 | $attr = $options[ 'dsn' ]; 110 | } 111 | else 112 | { 113 | throw new InvalidArgumentException('Invalid DSN option supplied'); 114 | } 115 | } 116 | else 117 | { 118 | if ( 119 | isset($options[ 'port' ]) && 120 | is_int($options[ 'port' ] * 1) 121 | ) 122 | { 123 | $port = $options[ 'port' ]; 124 | } 125 | 126 | $is_port = isset($port); 127 | 128 | switch ($this->type) 129 | { 130 | case 'mysql': 131 | $attr = [ 132 | 'driver' => 'mysql', 133 | 'dbname' => $options[ 'database_name' ] 134 | ]; 135 | 136 | if (isset($options[ 'socket' ])) 137 | { 138 | $attr[ 'unix_socket' ] = $options[ 'socket' ]; 139 | } 140 | else 141 | { 142 | $attr[ 'host' ] = $options[ 'server' ]; 143 | 144 | if ($is_port) 145 | { 146 | $attr[ 'port' ] = $port; 147 | } 148 | } 149 | 150 | break; 151 | 152 | case 'pgsql': 153 | $attr = [ 154 | 'driver' => 'pgsql', 155 | 'host' => $options[ 'server' ], 156 | 'dbname' => $options[ 'database_name' ] 157 | ]; 158 | 159 | if ($is_port) 160 | { 161 | $attr[ 'port' ] = $port; 162 | } 163 | 164 | break; 165 | 166 | case 'sybase': 167 | $attr = [ 168 | 'driver' => 'dblib', 169 | 'host' => $options[ 'server' ], 170 | 'dbname' => $options[ 'database_name' ] 171 | ]; 172 | 173 | if ($is_port) 174 | { 175 | $attr[ 'port' ] = $port; 176 | } 177 | 178 | break; 179 | 180 | case 'oracle': 181 | $attr = [ 182 | 'driver' => 'oci', 183 | 'dbname' => $options[ 'server' ] ? 184 | '//' . $options[ 'server' ] . ($is_port ? ':' . $port : ':1521') . '/' . $options[ 'database_name' ] : 185 | $options[ 'database_name' ] 186 | ]; 187 | 188 | if (isset($options[ 'charset' ])) 189 | { 190 | $attr[ 'charset' ] = $options[ 'charset' ]; 191 | } 192 | 193 | break; 194 | 195 | case 'mssql': 196 | if (isset($options[ 'driver' ]) && $options[ 'driver' ] === 'dblib') 197 | { 198 | $attr = [ 199 | 'driver' => 'dblib', 200 | 'host' => $options[ 'server' ] . ($is_port ? ':' . $port : ''), 201 | 'dbname' => $options[ 'database_name' ] 202 | ]; 203 | 204 | if (isset($options[ 'appname' ])) 205 | { 206 | $attr[ 'appname' ] = $options[ 'appname' ]; 207 | } 208 | 209 | if (isset($options[ 'charset' ])) 210 | { 211 | $attr[ 'charset' ] = $options[ 'charset' ]; 212 | } 213 | } 214 | else 215 | { 216 | $attr = [ 217 | 'driver' => 'sqlsrv', 218 | 'Server' => $options[ 'server' ] . ($is_port ? ',' . $port : ''), 219 | 'Database' => $options[ 'database_name' ] 220 | ]; 221 | 222 | if (isset($options[ 'appname' ])) 223 | { 224 | $attr[ 'APP' ] = $options[ 'appname' ]; 225 | } 226 | 227 | $config = [ 228 | 'ApplicationIntent', 229 | 'AttachDBFileName', 230 | 'Authentication', 231 | 'ColumnEncryption', 232 | 'ConnectionPooling', 233 | 'Encrypt', 234 | 'Failover_Partner', 235 | 'KeyStoreAuthentication', 236 | 'KeyStorePrincipalId', 237 | 'KeyStoreSecret', 238 | 'LoginTimeout', 239 | 'MultipleActiveResultSets', 240 | 'MultiSubnetFailover', 241 | 'Scrollable', 242 | 'TraceFile', 243 | 'TraceOn', 244 | 'TransactionIsolation', 245 | 'TransparentNetworkIPResolution', 246 | 'TrustServerCertificate', 247 | 'WSID', 248 | ]; 249 | 250 | foreach ($config as $value) 251 | { 252 | $keyname = strtolower(preg_replace(['/([a-z\d])([A-Z])/', '/([^_])([A-Z][a-z])/'], '$1_$2', $value)); 253 | 254 | if (isset($options[ $keyname ])) 255 | { 256 | $attr[ $value ] = $options[ $keyname ]; 257 | } 258 | } 259 | } 260 | 261 | break; 262 | 263 | case 'sqlite': 264 | $attr = [ 265 | 'driver' => 'sqlite', 266 | $options[ 'database_file' ] 267 | ]; 268 | 269 | break; 270 | } 271 | } 272 | 273 | if (!isset($attr)) 274 | { 275 | throw new InvalidArgumentException('Incorrect connection options'); 276 | } 277 | 278 | $driver = $attr[ 'driver' ]; 279 | 280 | if (!in_array($driver, PDO::getAvailableDrivers())) 281 | { 282 | throw new InvalidArgumentException("Unsupported PDO driver: {$driver}"); 283 | } 284 | 285 | unset($attr[ 'driver' ]); 286 | 287 | $stack = []; 288 | 289 | foreach ($attr as $key => $value) 290 | { 291 | $stack[] = is_int($key) ? $value : $key . '=' . $value; 292 | } 293 | 294 | $dsn = $driver . ':' . implode(';', $stack); 295 | 296 | if ( 297 | in_array($this->type, ['mysql', 'pgsql', 'sybase', 'mssql']) && 298 | isset($options[ 'charset' ]) 299 | ) 300 | { 301 | $commands[] = "SET NAMES '{$options[ 'charset' ]}'" . ( 302 | $this->type === 'mysql' && isset($options[ 'collation' ]) ? 303 | " COLLATE '{$options[ 'collation' ]}'" : '' 304 | ); 305 | } 306 | 307 | $this->dsn = $dsn; 308 | 309 | try { 310 | $this->pdo = new PDO( 311 | $dsn, 312 | isset($options[ 'username' ]) ? $options[ 'username' ] : null, 313 | isset($options[ 'password' ]) ? $options[ 'password' ] : null, 314 | $option 315 | ); 316 | 317 | foreach ($commands as $value) 318 | { 319 | $this->pdo->exec($value); 320 | } 321 | } 322 | catch (PDOException $e) { 323 | throw new PDOException($e->getMessage()); 324 | } 325 | } 326 | 327 | public function query($query, $map = []) 328 | { 329 | $raw = $this->raw($query, $map); 330 | 331 | $query = $this->buildRaw($raw, $map); 332 | 333 | return $this->exec($query, $map); 334 | } 335 | 336 | public function exec($query, $map = []) 337 | { 338 | $this->statement = null; 339 | 340 | if ($this->debug_mode) 341 | { 342 | echo $this->generate($query, $map); 343 | 344 | $this->debug_mode = false; 345 | 346 | return false; 347 | } 348 | 349 | if ($this->logging) 350 | { 351 | $this->logs[] = [$query, $map]; 352 | } 353 | else 354 | { 355 | $this->logs = [[$query, $map]]; 356 | } 357 | 358 | $statement = $this->pdo->prepare($query); 359 | 360 | if (!$statement) 361 | { 362 | $this->errorInfo = $this->pdo->errorInfo(); 363 | $this->statement = null; 364 | 365 | return false; 366 | } 367 | 368 | $this->statement = $statement; 369 | 370 | foreach ($map as $key => $value) 371 | { 372 | $statement->bindValue($key, $value[ 0 ], $value[ 1 ]); 373 | } 374 | 375 | $execute = $statement->execute(); 376 | 377 | $this->errorInfo = $statement->errorInfo(); 378 | 379 | if (!$execute) 380 | { 381 | $this->statement = null; 382 | } 383 | 384 | return $statement; 385 | } 386 | 387 | protected function generate($query, $map) 388 | { 389 | $identifier = [ 390 | 'mysql' => '`$1`', 391 | 'mssql' => '[$1]' 392 | ]; 393 | 394 | $query = preg_replace( 395 | '/"([a-zA-Z0-9_]+)"/i', 396 | isset($identifier[ $this->type ]) ? $identifier[ $this->type ] : '"$1"', 397 | $query 398 | ); 399 | 400 | foreach ($map as $key => $value) 401 | { 402 | if ($value[ 1 ] === PDO::PARAM_STR) 403 | { 404 | $replace = $this->quote($value[ 0 ]); 405 | } 406 | elseif ($value[ 1 ] === PDO::PARAM_NULL) 407 | { 408 | $replace = 'NULL'; 409 | } 410 | elseif ($value[ 1 ] === PDO::PARAM_LOB) 411 | { 412 | $replace = '{LOB_DATA}'; 413 | } 414 | else 415 | { 416 | $replace = $value[ 0 ]; 417 | } 418 | 419 | $query = str_replace($key, $replace, $query); 420 | } 421 | 422 | return $query; 423 | } 424 | 425 | public static function raw($string, $map = []) 426 | { 427 | $raw = new Raw(); 428 | 429 | $raw->map = $map; 430 | $raw->value = $string; 431 | 432 | return $raw; 433 | } 434 | 435 | protected function isRaw($object) 436 | { 437 | return $object instanceof Raw; 438 | } 439 | 440 | protected function buildRaw($raw, &$map) 441 | { 442 | if (!$this->isRaw($raw)) 443 | { 444 | return false; 445 | } 446 | 447 | $query = preg_replace_callback( 448 | '/(([`\']).*?)?((FROM|TABLE|INTO|UPDATE|JOIN)\s*)?\<(([a-zA-Z0-9_]+)(\.[a-zA-Z0-9_]+)?)\>(.*?\2)?/i', 449 | function ($matches) 450 | { 451 | if (!empty($matches[ 2 ]) && isset($matches[ 8 ])) 452 | { 453 | return $matches[ 0 ]; 454 | } 455 | 456 | if (!empty($matches[ 4 ])) 457 | { 458 | return $matches[ 1 ] . $matches[ 4 ] . ' ' . $this->tableQuote($matches[ 5 ]); 459 | } 460 | 461 | return $matches[ 1 ] . $this->columnQuote($matches[ 5 ]); 462 | }, 463 | $raw->value); 464 | 465 | $raw_map = $raw->map; 466 | 467 | if (!empty($raw_map)) 468 | { 469 | foreach ($raw_map as $key => $value) 470 | { 471 | $map[ $key ] = $this->typeMap($value, gettype($value)); 472 | } 473 | } 474 | 475 | return $query; 476 | } 477 | 478 | public function quote($string) 479 | { 480 | return $this->pdo->quote($string); 481 | } 482 | 483 | protected function tableQuote($table) 484 | { 485 | if (!preg_match('/^[a-zA-Z0-9_]+$/i', $table)) 486 | { 487 | throw new InvalidArgumentException("Incorrect table name \"$table\""); 488 | } 489 | 490 | return '"' . $this->prefix . $table . '"'; 491 | } 492 | 493 | protected function mapKey() 494 | { 495 | return ':MeDoO_' . $this->guid++ . '_mEdOo'; 496 | } 497 | 498 | protected function typeMap($value, $type) 499 | { 500 | $map = [ 501 | 'NULL' => PDO::PARAM_NULL, 502 | 'integer' => PDO::PARAM_INT, 503 | 'double' => PDO::PARAM_STR, 504 | 'boolean' => PDO::PARAM_BOOL, 505 | 'string' => PDO::PARAM_STR, 506 | 'object' => PDO::PARAM_STR, 507 | 'resource' => PDO::PARAM_LOB 508 | ]; 509 | 510 | if ($type === 'boolean') 511 | { 512 | $value = ($value ? '1' : '0'); 513 | } 514 | elseif ($type === 'NULL') 515 | { 516 | $value = null; 517 | } 518 | 519 | return [$value, $map[ $type ]]; 520 | } 521 | 522 | protected function columnQuote($string) 523 | { 524 | if (!preg_match('/^[a-zA-Z0-9_]+(\.?[a-zA-Z0-9_]+)?$/i', $string)) 525 | { 526 | throw new InvalidArgumentException("Incorrect column name \"$string\""); 527 | } 528 | 529 | if (strpos($string, '.') !== false) 530 | { 531 | return '"' . $this->prefix . str_replace('.', '"."', $string) . '"'; 532 | } 533 | 534 | return '"' . $string . '"'; 535 | } 536 | 537 | protected function columnPush(&$columns, &$map, $root, $is_join = false) 538 | { 539 | if ($columns === '*') 540 | { 541 | return $columns; 542 | } 543 | 544 | $stack = []; 545 | 546 | if (is_string($columns)) 547 | { 548 | $columns = [$columns]; 549 | } 550 | 551 | foreach ($columns as $key => $value) 552 | { 553 | if (!is_int($key) && is_array($value) && $root && count(array_keys($columns)) === 1) 554 | { 555 | $stack[] = $this->columnQuote($key); 556 | 557 | $stack[] = $this->columnPush($value, $map, false, $is_join); 558 | } 559 | elseif (is_array($value)) 560 | { 561 | $stack[] = $this->columnPush($value, $map, false, $is_join); 562 | } 563 | elseif (!is_int($key) && $raw = $this->buildRaw($value, $map)) 564 | { 565 | preg_match('/(?[a-zA-Z0-9_\.]+)(\s*\[(?(String|Bool|Int|Number))\])?/i', $key, $match); 566 | 567 | $stack[] = $raw . ' AS ' . $this->columnQuote($match[ 'column' ]); 568 | } 569 | elseif (is_int($key) && is_string($value)) 570 | { 571 | if ($is_join && strpos($value, '*') !== false) 572 | { 573 | throw new InvalidArgumentException('Cannot use table.* to select all columns while joining table'); 574 | } 575 | 576 | preg_match('/(?[a-zA-Z0-9_\.]+)(?:\s*\((?[a-zA-Z0-9_]+)\))?(?:\s*\[(?(?:String|Bool|Int|Number|Object|JSON))\])?/i', $value, $match); 577 | 578 | if (!empty($match[ 'alias' ])) 579 | { 580 | $stack[] = $this->columnQuote($match[ 'column' ]) . ' AS ' . $this->columnQuote($match[ 'alias' ]); 581 | 582 | $columns[ $key ] = $match[ 'alias' ]; 583 | 584 | if (!empty($match[ 'type' ])) 585 | { 586 | $columns[ $key ] .= ' [' . $match[ 'type' ] . ']'; 587 | } 588 | } 589 | else 590 | { 591 | $stack[] = $this->columnQuote($match[ 'column' ]); 592 | } 593 | } 594 | } 595 | 596 | return implode(',', $stack); 597 | } 598 | 599 | protected function arrayQuote($array) 600 | { 601 | $stack = []; 602 | 603 | foreach ($array as $value) 604 | { 605 | $stack[] = is_int($value) ? $value : $this->pdo->quote($value); 606 | } 607 | 608 | return implode(',', $stack); 609 | } 610 | 611 | protected function innerConjunct($data, $map, $conjunctor, $outer_conjunctor) 612 | { 613 | $stack = []; 614 | 615 | foreach ($data as $value) 616 | { 617 | $stack[] = '(' . $this->dataImplode($value, $map, $conjunctor) . ')'; 618 | } 619 | 620 | return implode($outer_conjunctor . ' ', $stack); 621 | } 622 | 623 | protected function dataImplode($data, &$map, $conjunctor) 624 | { 625 | $stack = []; 626 | 627 | foreach ($data as $key => $value) 628 | { 629 | $type = gettype($value); 630 | 631 | if ( 632 | $type === 'array' && 633 | preg_match("/^(AND|OR)(\s+#.*)?$/", $key, $relation_match) 634 | ) 635 | { 636 | $relationship = $relation_match[ 1 ]; 637 | 638 | $stack[] = $value !== array_keys(array_keys($value)) ? 639 | '(' . $this->dataImplode($value, $map, ' ' . $relationship) . ')' : 640 | '(' . $this->innerConjunct($value, $map, ' ' . $relationship, $conjunctor) . ')'; 641 | 642 | continue; 643 | } 644 | 645 | $map_key = $this->mapKey(); 646 | 647 | if ( 648 | is_int($key) && 649 | preg_match('/([a-zA-Z0-9_\.]+)\[(?\>\=?|\<\=?|\!?\=)\]([a-zA-Z0-9_\.]+)/i', $value, $match) 650 | ) 651 | { 652 | $stack[] = $this->columnQuote($match[ 1 ]) . ' ' . $match[ 'operator' ] . ' ' . $this->columnQuote($match[ 3 ]); 653 | } 654 | else 655 | { 656 | preg_match('/([a-zA-Z0-9_\.]+)(\[(?\>\=?|\<\=?|\!|\<\>|\>\<|\!?~|REGEXP)\])?/i', $key, $match); 657 | $column = $this->columnQuote($match[ 1 ]); 658 | 659 | if (isset($match[ 'operator' ])) 660 | { 661 | $operator = $match[ 'operator' ]; 662 | 663 | if (in_array($operator, ['>', '>=', '<', '<='])) 664 | { 665 | $condition = $column . ' ' . $operator . ' '; 666 | 667 | if (is_numeric($value)) 668 | { 669 | $condition .= $map_key; 670 | $map[ $map_key ] = [$value, is_float($value) ? PDO::PARAM_STR : PDO::PARAM_INT]; 671 | } 672 | elseif ($raw = $this->buildRaw($value, $map)) 673 | { 674 | $condition .= $raw; 675 | } 676 | else 677 | { 678 | $condition .= $map_key; 679 | $map[ $map_key ] = [$value, PDO::PARAM_STR]; 680 | } 681 | 682 | $stack[] = $condition; 683 | } 684 | elseif ($operator === '!') 685 | { 686 | switch ($type) 687 | { 688 | case 'NULL': 689 | $stack[] = $column . ' IS NOT NULL'; 690 | break; 691 | 692 | case 'array': 693 | $placeholders = []; 694 | 695 | foreach ($value as $index => $item) 696 | { 697 | $stack_key = $map_key . $index . '_i'; 698 | 699 | $placeholders[] = $stack_key; 700 | $map[ $stack_key ] = $this->typeMap($item, gettype($item)); 701 | } 702 | 703 | $stack[] = $column . ' NOT IN (' . implode(', ', $placeholders) . ')'; 704 | break; 705 | 706 | case 'object': 707 | if ($raw = $this->buildRaw($value, $map)) 708 | { 709 | $stack[] = $column . ' != ' . $raw; 710 | } 711 | break; 712 | 713 | case 'integer': 714 | case 'double': 715 | case 'boolean': 716 | case 'string': 717 | $stack[] = $column . ' != ' . $map_key; 718 | $map[ $map_key ] = $this->typeMap($value, $type); 719 | break; 720 | } 721 | } 722 | elseif ($operator === '~' || $operator === '!~') 723 | { 724 | if ($type !== 'array') 725 | { 726 | $value = [ $value ]; 727 | } 728 | 729 | $connector = ' OR '; 730 | $data = array_values($value); 731 | 732 | if (is_array($data[ 0 ])) 733 | { 734 | if (isset($value[ 'AND' ]) || isset($value[ 'OR' ])) 735 | { 736 | $connector = ' ' . array_keys($value)[ 0 ] . ' '; 737 | $value = $data[ 0 ]; 738 | } 739 | } 740 | 741 | $like_clauses = []; 742 | 743 | foreach ($value as $index => $item) 744 | { 745 | $item = strval($item); 746 | 747 | if (!preg_match('/(\[.+\]|[\*\?\!\%#^-_]|%.+|.+%)/', $item)) 748 | { 749 | $item = '%' . $item . '%'; 750 | } 751 | 752 | $like_clauses[] = $column . ($operator === '!~' ? ' NOT' : '') . ' LIKE ' . $map_key . 'L' . $index; 753 | $map[ $map_key . 'L' . $index ] = [$item, PDO::PARAM_STR]; 754 | } 755 | 756 | $stack[] = '(' . implode($connector, $like_clauses) . ')'; 757 | } 758 | elseif ($operator === '<>' || $operator === '><') 759 | { 760 | if ($type === 'array') 761 | { 762 | if ($operator === '><') 763 | { 764 | $column .= ' NOT'; 765 | } 766 | 767 | $stack[] = '(' . $column . ' BETWEEN ' . $map_key . 'a AND ' . $map_key . 'b)'; 768 | 769 | $data_type = (is_numeric($value[ 0 ]) && is_numeric($value[ 1 ])) ? PDO::PARAM_INT : PDO::PARAM_STR; 770 | 771 | $map[ $map_key . 'a' ] = [$value[ 0 ], $data_type]; 772 | $map[ $map_key . 'b' ] = [$value[ 1 ], $data_type]; 773 | } 774 | } 775 | elseif ($operator === 'REGEXP') 776 | { 777 | $stack[] = $column . ' REGEXP ' . $map_key; 778 | $map[ $map_key ] = [$value, PDO::PARAM_STR]; 779 | } 780 | } 781 | else 782 | { 783 | switch ($type) 784 | { 785 | case 'NULL': 786 | $stack[] = $column . ' IS NULL'; 787 | break; 788 | 789 | case 'array': 790 | $placeholders = []; 791 | 792 | foreach ($value as $index => $item) 793 | { 794 | $stack_key = $map_key . $index . '_i'; 795 | 796 | $placeholders[] = $stack_key; 797 | $map[ $stack_key ] = $this->typeMap($item, gettype($item)); 798 | } 799 | 800 | $stack[] = $column . ' IN (' . implode(', ', $placeholders) . ')'; 801 | break; 802 | 803 | case 'object': 804 | if ($raw = $this->buildRaw($value, $map)) 805 | { 806 | $stack[] = $column . ' = ' . $raw; 807 | } 808 | break; 809 | 810 | case 'integer': 811 | case 'double': 812 | case 'boolean': 813 | case 'string': 814 | $stack[] = $column . ' = ' . $map_key; 815 | $map[ $map_key ] = $this->typeMap($value, $type); 816 | break; 817 | } 818 | } 819 | } 820 | } 821 | 822 | return implode($conjunctor . ' ', $stack); 823 | } 824 | 825 | protected function whereClause($where, &$map) 826 | { 827 | $where_clause = ''; 828 | 829 | if (is_array($where)) 830 | { 831 | $where_keys = array_keys($where); 832 | 833 | $conditions = array_diff_key($where, array_flip( 834 | ['GROUP', 'ORDER', 'HAVING', 'LIMIT', 'LIKE', 'MATCH'] 835 | )); 836 | 837 | if (!empty($conditions)) 838 | { 839 | $where_clause = ' WHERE ' . $this->dataImplode($conditions, $map, ' AND'); 840 | } 841 | 842 | if (isset($where[ 'MATCH' ]) && $this->type === 'mysql') 843 | { 844 | $MATCH = $where[ 'MATCH' ]; 845 | 846 | if (is_array($MATCH) && isset($MATCH[ 'columns' ], $MATCH[ 'keyword' ])) 847 | { 848 | $mode = ''; 849 | 850 | $mode_array = [ 851 | 'natural' => 'IN NATURAL LANGUAGE MODE', 852 | 'natural+query' => 'IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION', 853 | 'boolean' => 'IN BOOLEAN MODE', 854 | 'query' => 'WITH QUERY EXPANSION' 855 | ]; 856 | 857 | if (isset($MATCH[ 'mode' ], $mode_array[ $MATCH[ 'mode' ] ])) 858 | { 859 | $mode = ' ' . $mode_array[ $MATCH[ 'mode' ] ]; 860 | } 861 | 862 | $columns = implode(', ', array_map([$this, 'columnQuote'], $MATCH[ 'columns' ])); 863 | $map_key = $this->mapKey(); 864 | $map[ $map_key ] = [$MATCH[ 'keyword' ], PDO::PARAM_STR]; 865 | 866 | $where_clause .= ($where_clause !== '' ? ' AND ' : ' WHERE') . ' MATCH (' . $columns . ') AGAINST (' . $map_key . $mode . ')'; 867 | } 868 | } 869 | 870 | if (isset($where[ 'GROUP' ])) 871 | { 872 | $GROUP = $where[ 'GROUP' ]; 873 | 874 | if (is_array($GROUP)) 875 | { 876 | $stack = []; 877 | 878 | foreach ($GROUP as $column => $value) 879 | { 880 | $stack[] = $this->columnQuote($value); 881 | } 882 | 883 | $where_clause .= ' GROUP BY ' . implode(',', $stack); 884 | } 885 | elseif ($raw = $this->buildRaw($GROUP, $map)) 886 | { 887 | $where_clause .= ' GROUP BY ' . $raw; 888 | } 889 | else 890 | { 891 | $where_clause .= ' GROUP BY ' . $this->columnQuote($GROUP); 892 | } 893 | 894 | if (isset($where[ 'HAVING' ])) 895 | { 896 | if ($raw = $this->buildRaw($where[ 'HAVING' ], $map)) 897 | { 898 | $where_clause .= ' HAVING ' . $raw; 899 | } 900 | else 901 | { 902 | $where_clause .= ' HAVING ' . $this->dataImplode($where[ 'HAVING' ], $map, ' AND'); 903 | } 904 | } 905 | } 906 | 907 | if (isset($where[ 'ORDER' ])) 908 | { 909 | $ORDER = $where[ 'ORDER' ]; 910 | 911 | if (is_array($ORDER)) 912 | { 913 | $stack = []; 914 | 915 | foreach ($ORDER as $column => $value) 916 | { 917 | if (is_array($value)) 918 | { 919 | $stack[] = 'FIELD(' . $this->columnQuote($column) . ', ' . $this->arrayQuote($value) . ')'; 920 | } 921 | elseif ($value === 'ASC' || $value === 'DESC') 922 | { 923 | $stack[] = $this->columnQuote($column) . ' ' . $value; 924 | } 925 | elseif (is_int($column)) 926 | { 927 | $stack[] = $this->columnQuote($value); 928 | } 929 | } 930 | 931 | $where_clause .= ' ORDER BY ' . implode(',', $stack); 932 | } 933 | elseif ($raw = $this->buildRaw($ORDER, $map)) 934 | { 935 | $where_clause .= ' ORDER BY ' . $raw; 936 | } 937 | else 938 | { 939 | $where_clause .= ' ORDER BY ' . $this->columnQuote($ORDER); 940 | } 941 | 942 | if ( 943 | isset($where[ 'LIMIT' ]) && 944 | in_array($this->type, ['oracle', 'mssql']) 945 | ) 946 | { 947 | $LIMIT = $where[ 'LIMIT' ]; 948 | 949 | if (is_numeric($LIMIT)) 950 | { 951 | $LIMIT = [0, $LIMIT]; 952 | } 953 | 954 | if ( 955 | is_array($LIMIT) && 956 | is_numeric($LIMIT[ 0 ]) && 957 | is_numeric($LIMIT[ 1 ]) 958 | ) 959 | { 960 | $where_clause .= ' OFFSET ' . $LIMIT[ 0 ] . ' ROWS FETCH NEXT ' . $LIMIT[ 1 ] . ' ROWS ONLY'; 961 | } 962 | } 963 | } 964 | 965 | if (isset($where[ 'LIMIT' ]) && !in_array($this->type, ['oracle', 'mssql'])) 966 | { 967 | $LIMIT = $where[ 'LIMIT' ]; 968 | 969 | if (is_numeric($LIMIT)) 970 | { 971 | $where_clause .= ' LIMIT ' . $LIMIT; 972 | } 973 | elseif ( 974 | is_array($LIMIT) && 975 | is_numeric($LIMIT[ 0 ]) && 976 | is_numeric($LIMIT[ 1 ]) 977 | ) 978 | { 979 | $where_clause .= ' LIMIT ' . $LIMIT[ 1 ] . ' OFFSET ' . $LIMIT[ 0 ]; 980 | } 981 | } 982 | } 983 | elseif ($raw = $this->buildRaw($where, $map)) 984 | { 985 | $where_clause .= ' ' . $raw; 986 | } 987 | 988 | return $where_clause; 989 | } 990 | 991 | protected function selectContext($table, &$map, $join, &$columns = null, $where = null, $column_fn = null) 992 | { 993 | preg_match('/(?[a-zA-Z0-9_]+)\s*\((?[a-zA-Z0-9_]+)\)/i', $table, $table_match); 994 | 995 | if (isset($table_match[ 'table' ], $table_match[ 'alias' ])) 996 | { 997 | $table = $this->tableQuote($table_match[ 'table' ]); 998 | 999 | $table_query = $table . ' AS ' . $this->tableQuote($table_match[ 'alias' ]); 1000 | } 1001 | else 1002 | { 1003 | $table = $this->tableQuote($table); 1004 | 1005 | $table_query = $table; 1006 | } 1007 | 1008 | $is_join = false; 1009 | $join_key = is_array($join) ? array_keys($join) : null; 1010 | 1011 | if ( 1012 | isset($join_key[ 0 ]) && 1013 | strpos($join_key[ 0 ], '[') === 0 1014 | ) 1015 | { 1016 | $is_join = true; 1017 | $table_query .= ' ' . $this->buildJoin($table, $join); 1018 | } 1019 | else 1020 | { 1021 | if (is_null($columns)) 1022 | { 1023 | if ( 1024 | !is_null($where) || 1025 | (is_array($join) && isset($column_fn)) 1026 | ) 1027 | { 1028 | $where = $join; 1029 | $columns = null; 1030 | } 1031 | else 1032 | { 1033 | $where = null; 1034 | $columns = $join; 1035 | } 1036 | } 1037 | else 1038 | { 1039 | $where = $columns; 1040 | $columns = $join; 1041 | } 1042 | } 1043 | 1044 | if (isset($column_fn)) 1045 | { 1046 | if ($column_fn === 1) 1047 | { 1048 | $column = '1'; 1049 | 1050 | if (is_null($where)) 1051 | { 1052 | $where = $columns; 1053 | } 1054 | } 1055 | elseif ($raw = $this->buildRaw($column_fn, $map)) 1056 | { 1057 | $column = $raw; 1058 | } 1059 | else 1060 | { 1061 | if (empty($columns) || $this->isRaw($columns)) 1062 | { 1063 | $columns = '*'; 1064 | $where = $join; 1065 | } 1066 | 1067 | $column = $column_fn . '(' . $this->columnPush($columns, $map, true) . ')'; 1068 | } 1069 | } 1070 | else 1071 | { 1072 | $column = $this->columnPush($columns, $map, true, $is_join); 1073 | } 1074 | 1075 | return 'SELECT ' . $column . ' FROM ' . $table_query . $this->whereClause($where, $map); 1076 | } 1077 | 1078 | protected function buildJoin($table, $join) 1079 | { 1080 | $table_join = []; 1081 | 1082 | $join_array = [ 1083 | '>' => 'LEFT', 1084 | '<' => 'RIGHT', 1085 | '<>' => 'FULL', 1086 | '><' => 'INNER' 1087 | ]; 1088 | 1089 | foreach($join as $sub_table => $relation) 1090 | { 1091 | preg_match('/(\[(?\<\>?|\>\[a-zA-Z0-9_]+)\s?(\((?[a-zA-Z0-9_]+)\))?/', $sub_table, $match); 1092 | 1093 | if ($match[ 'join' ] !== '' && $match[ 'table' ] !== '') 1094 | { 1095 | if (is_string($relation)) 1096 | { 1097 | $relation = 'USING ("' . $relation . '")'; 1098 | } 1099 | 1100 | if (is_array($relation)) 1101 | { 1102 | // For ['column1', 'column2'] 1103 | if (isset($relation[ 0 ])) 1104 | { 1105 | $relation = 'USING ("' . implode('", "', $relation) . '")'; 1106 | } 1107 | else 1108 | { 1109 | $joins = []; 1110 | 1111 | foreach ($relation as $key => $value) 1112 | { 1113 | $joins[] = ( 1114 | strpos($key, '.') > 0 ? 1115 | // For ['tableB.column' => 'column'] 1116 | $this->columnQuote($key) : 1117 | 1118 | // For ['column1' => 'column2'] 1119 | $table . '."' . $key . '"' 1120 | ) . 1121 | ' = ' . 1122 | $this->tableQuote(isset($match[ 'alias' ]) ? $match[ 'alias' ] : $match[ 'table' ]) . '."' . $value . '"'; 1123 | } 1124 | 1125 | $relation = 'ON ' . implode(' AND ', $joins); 1126 | } 1127 | } 1128 | 1129 | $table_name = $this->tableQuote($match[ 'table' ]) . ' '; 1130 | 1131 | if (isset($match[ 'alias' ])) 1132 | { 1133 | $table_name .= 'AS ' . $this->tableQuote($match[ 'alias' ]) . ' '; 1134 | } 1135 | 1136 | $table_join[] = $join_array[ $match[ 'join' ] ] . ' JOIN ' . $table_name . $relation; 1137 | } 1138 | } 1139 | 1140 | return implode(' ', $table_join); 1141 | } 1142 | 1143 | protected function columnMap($columns, &$stack, $root) 1144 | { 1145 | if ($columns === '*') 1146 | { 1147 | return $stack; 1148 | } 1149 | 1150 | foreach ($columns as $key => $value) 1151 | { 1152 | if (is_int($key)) 1153 | { 1154 | preg_match('/([a-zA-Z0-9_]+\.)?(?[a-zA-Z0-9_]+)(?:\s*\((?[a-zA-Z0-9_]+)\))?(?:\s*\[(?(?:String|Bool|Int|Number|Object|JSON))\])?/i', $value, $key_match); 1155 | 1156 | $column_key = !empty($key_match[ 'alias' ]) ? 1157 | $key_match[ 'alias' ] : 1158 | $key_match[ 'column' ]; 1159 | 1160 | if (isset($key_match[ 'type' ])) 1161 | { 1162 | $stack[ $value ] = [$column_key, $key_match[ 'type' ]]; 1163 | } 1164 | else 1165 | { 1166 | $stack[ $value ] = [$column_key, 'String']; 1167 | } 1168 | } 1169 | elseif ($this->isRaw($value)) 1170 | { 1171 | preg_match('/([a-zA-Z0-9_]+\.)?(?[a-zA-Z0-9_]+)(\s*\[(?(String|Bool|Int|Number))\])?/i', $key, $key_match); 1172 | 1173 | $column_key = $key_match[ 'column' ]; 1174 | 1175 | if (isset($key_match[ 'type' ])) 1176 | { 1177 | $stack[ $key ] = [$column_key, $key_match[ 'type' ]]; 1178 | } 1179 | else 1180 | { 1181 | $stack[ $key ] = [$column_key, 'String']; 1182 | } 1183 | } 1184 | elseif (!is_int($key) && is_array($value)) 1185 | { 1186 | if ($root && count(array_keys($columns)) === 1) 1187 | { 1188 | $stack[ $key ] = [$key, 'String']; 1189 | } 1190 | 1191 | $this->columnMap($value, $stack, false); 1192 | } 1193 | } 1194 | 1195 | return $stack; 1196 | } 1197 | 1198 | protected function dataMap($data, $columns, $column_map, &$stack, $root, &$result) 1199 | { 1200 | if ($root) 1201 | { 1202 | $columns_key = array_keys($columns); 1203 | 1204 | if (count($columns_key) === 1 && is_array($columns[$columns_key[0]])) 1205 | { 1206 | $index_key = array_keys($columns)[0]; 1207 | $data_key = preg_replace("/^[a-zA-Z0-9_]+\./i", "", $index_key); 1208 | 1209 | $current_stack = []; 1210 | 1211 | foreach ($data as $item) 1212 | { 1213 | $this->dataMap($data, $columns[ $index_key ], $column_map, $current_stack, false, $result); 1214 | 1215 | $index = $data[ $data_key ]; 1216 | 1217 | $result[ $index ] = $current_stack; 1218 | } 1219 | } 1220 | else 1221 | { 1222 | $current_stack = []; 1223 | 1224 | $this->dataMap($data, $columns, $column_map, $current_stack, false, $result); 1225 | 1226 | $result[] = $current_stack; 1227 | } 1228 | 1229 | return; 1230 | } 1231 | 1232 | foreach ($columns as $key => $value) 1233 | { 1234 | $isRaw = $this->isRaw($value); 1235 | 1236 | if (is_int($key) || $isRaw) 1237 | { 1238 | $map = $column_map[ $isRaw ? $key : $value ]; 1239 | 1240 | $column_key = $map[ 0 ]; 1241 | 1242 | $item = $data[ $column_key ]; 1243 | 1244 | if (isset($map[ 1 ])) 1245 | { 1246 | if ($isRaw && in_array($map[ 1 ], ['Object', 'JSON'])) 1247 | { 1248 | continue; 1249 | } 1250 | 1251 | if (is_null($item)) 1252 | { 1253 | $stack[ $column_key ] = null; 1254 | continue; 1255 | } 1256 | 1257 | switch ($map[ 1 ]) 1258 | { 1259 | case 'Number': 1260 | $stack[ $column_key ] = (double) $item; 1261 | break; 1262 | 1263 | case 'Int': 1264 | $stack[ $column_key ] = (int) $item; 1265 | break; 1266 | 1267 | case 'Bool': 1268 | $stack[ $column_key ] = (bool) $item; 1269 | break; 1270 | 1271 | case 'Object': 1272 | $stack[ $column_key ] = unserialize($item); 1273 | break; 1274 | 1275 | case 'JSON': 1276 | $stack[ $column_key ] = json_decode($item, true); 1277 | break; 1278 | 1279 | case 'String': 1280 | $stack[ $column_key ] = $item; 1281 | break; 1282 | } 1283 | } 1284 | else 1285 | { 1286 | $stack[ $column_key ] = $item; 1287 | } 1288 | } 1289 | else 1290 | { 1291 | $current_stack = []; 1292 | 1293 | $this->dataMap($data, $value, $column_map, $current_stack, false, $result); 1294 | 1295 | $stack[ $key ] = $current_stack; 1296 | } 1297 | } 1298 | } 1299 | 1300 | public function create($table, $columns, $options = null) 1301 | { 1302 | $stack = []; 1303 | 1304 | $tableName = $this->prefix . $table; 1305 | 1306 | foreach ($columns as $name => $definition) 1307 | { 1308 | if (is_int($name)) 1309 | { 1310 | $stack[] = preg_replace('/\<([a-zA-Z0-9_]+)\>/i', '"$1"', $definition); 1311 | } 1312 | elseif (is_array($definition)) 1313 | { 1314 | $stack[] = $name . ' ' . implode(' ', $definition); 1315 | } 1316 | elseif (is_string($definition)) 1317 | { 1318 | $stack[] = $name . ' ' . $this->query($definition); 1319 | } 1320 | } 1321 | 1322 | $table_option = ''; 1323 | 1324 | if (is_array($options)) 1325 | { 1326 | $option_stack = []; 1327 | 1328 | foreach ($options as $key => $value) 1329 | { 1330 | if (is_string($value) || is_int($value)) 1331 | { 1332 | $option_stack[] = "$key = $value"; 1333 | } 1334 | } 1335 | 1336 | $table_option = ' ' . implode(', ', $option_stack); 1337 | } 1338 | elseif (is_string($options)) 1339 | { 1340 | $table_option = ' ' . $options; 1341 | } 1342 | 1343 | return $this->exec("CREATE TABLE IF NOT EXISTS $tableName (" . implode(', ', $stack) . ")$table_option"); 1344 | } 1345 | 1346 | public function drop($table) 1347 | { 1348 | $tableName = $this->prefix . $table; 1349 | 1350 | return $this->exec("DROP TABLE IF EXISTS $tableName"); 1351 | } 1352 | 1353 | public function select($table, $join, $columns = null, $where = null) 1354 | { 1355 | $map = []; 1356 | $result = []; 1357 | $column_map = []; 1358 | 1359 | $index = 0; 1360 | 1361 | $column = $where === null ? $join : $columns; 1362 | 1363 | $is_single = (is_string($column) && $column !== '*'); 1364 | 1365 | $query = $this->exec($this->selectContext($table, $map, $join, $columns, $where), $map); 1366 | 1367 | $this->columnMap($columns, $column_map, true); 1368 | 1369 | if (!$this->statement) 1370 | { 1371 | return false; 1372 | } 1373 | 1374 | if ($columns === '*') 1375 | { 1376 | return $query->fetchAll(PDO::FETCH_ASSOC); 1377 | } 1378 | 1379 | while ($data = $query->fetch(PDO::FETCH_ASSOC)) 1380 | { 1381 | $current_stack = []; 1382 | 1383 | $this->dataMap($data, $columns, $column_map, $current_stack, true, $result); 1384 | } 1385 | 1386 | if ($is_single) 1387 | { 1388 | $single_result = []; 1389 | $result_key = $column_map[ $column ][ 0 ]; 1390 | 1391 | foreach ($result as $item) 1392 | { 1393 | $single_result[] = $item[ $result_key ]; 1394 | } 1395 | 1396 | return $single_result; 1397 | } 1398 | 1399 | return $result; 1400 | } 1401 | 1402 | public function insert($table, $datas) 1403 | { 1404 | $stack = []; 1405 | $columns = []; 1406 | $fields = []; 1407 | $map = []; 1408 | 1409 | if (!isset($datas[ 0 ])) 1410 | { 1411 | $datas = [$datas]; 1412 | } 1413 | 1414 | foreach ($datas as $data) 1415 | { 1416 | foreach ($data as $key => $value) 1417 | { 1418 | $columns[] = $key; 1419 | } 1420 | } 1421 | 1422 | $columns = array_unique($columns); 1423 | 1424 | foreach ($datas as $data) 1425 | { 1426 | $values = []; 1427 | 1428 | foreach ($columns as $key) 1429 | { 1430 | if ($raw = $this->buildRaw($data[ $key ], $map)) 1431 | { 1432 | $values[] = $raw; 1433 | continue; 1434 | } 1435 | 1436 | $map_key = $this->mapKey(); 1437 | 1438 | $values[] = $map_key; 1439 | 1440 | if (!isset($data[ $key ])) 1441 | { 1442 | $map[ $map_key ] = [null, PDO::PARAM_NULL]; 1443 | } 1444 | else 1445 | { 1446 | $value = $data[ $key ]; 1447 | 1448 | $type = gettype($value); 1449 | 1450 | switch ($type) 1451 | { 1452 | case 'array': 1453 | $map[ $map_key ] = [ 1454 | strpos($key, '[JSON]') === strlen($key) - 6 ? 1455 | json_encode($value) : 1456 | serialize($value), 1457 | PDO::PARAM_STR 1458 | ]; 1459 | break; 1460 | 1461 | case 'object': 1462 | $value = serialize($value); 1463 | 1464 | case 'NULL': 1465 | case 'resource': 1466 | case 'boolean': 1467 | case 'integer': 1468 | case 'double': 1469 | case 'string': 1470 | $map[ $map_key ] = $this->typeMap($value, $type); 1471 | break; 1472 | } 1473 | } 1474 | } 1475 | 1476 | $stack[] = '(' . implode(', ', $values) . ')'; 1477 | } 1478 | 1479 | foreach ($columns as $key) 1480 | { 1481 | $fields[] = $this->columnQuote(preg_replace("/(\s*\[JSON\]$)/i", '', $key)); 1482 | } 1483 | 1484 | return $this->exec('INSERT INTO ' . $this->tableQuote($table) . ' (' . implode(', ', $fields) . ') VALUES ' . implode(', ', $stack), $map); 1485 | } 1486 | 1487 | public function update($table, $data, $where = null) 1488 | { 1489 | $fields = []; 1490 | $map = []; 1491 | 1492 | foreach ($data as $key => $value) 1493 | { 1494 | $column = $this->columnQuote(preg_replace("/(\s*\[(JSON|\+|\-|\*|\/)\]$)/i", '', $key)); 1495 | 1496 | if ($raw = $this->buildRaw($value, $map)) 1497 | { 1498 | $fields[] = $column . ' = ' . $raw; 1499 | continue; 1500 | } 1501 | 1502 | $map_key = $this->mapKey(); 1503 | 1504 | preg_match('/(?[a-zA-Z0-9_]+)(\[(?\+|\-|\*|\/)\])?/i', $key, $match); 1505 | 1506 | if (isset($match[ 'operator' ])) 1507 | { 1508 | if (is_numeric($value)) 1509 | { 1510 | $fields[] = $column . ' = ' . $column . ' ' . $match[ 'operator' ] . ' ' . $value; 1511 | } 1512 | } 1513 | else 1514 | { 1515 | $fields[] = $column . ' = ' . $map_key; 1516 | 1517 | $type = gettype($value); 1518 | 1519 | switch ($type) 1520 | { 1521 | case 'array': 1522 | $map[ $map_key ] = [ 1523 | strpos($key, '[JSON]') === strlen($key) - 6 ? 1524 | json_encode($value) : 1525 | serialize($value), 1526 | PDO::PARAM_STR 1527 | ]; 1528 | break; 1529 | 1530 | case 'object': 1531 | $value = serialize($value); 1532 | 1533 | case 'NULL': 1534 | case 'resource': 1535 | case 'boolean': 1536 | case 'integer': 1537 | case 'double': 1538 | case 'string': 1539 | $map[ $map_key ] = $this->typeMap($value, $type); 1540 | break; 1541 | } 1542 | } 1543 | } 1544 | 1545 | return $this->exec('UPDATE ' . $this->tableQuote($table) . ' SET ' . implode(', ', $fields) . $this->whereClause($where, $map), $map); 1546 | } 1547 | 1548 | public function delete($table, $where) 1549 | { 1550 | $map = []; 1551 | 1552 | return $this->exec('DELETE FROM ' . $this->tableQuote($table) . $this->whereClause($where, $map), $map); 1553 | } 1554 | 1555 | public function replace($table, $columns, $where = null) 1556 | { 1557 | if (!is_array($columns) || empty($columns)) 1558 | { 1559 | return false; 1560 | } 1561 | 1562 | $map = []; 1563 | $stack = []; 1564 | 1565 | foreach ($columns as $column => $replacements) 1566 | { 1567 | if (is_array($replacements)) 1568 | { 1569 | foreach ($replacements as $old => $new) 1570 | { 1571 | $map_key = $this->mapKey(); 1572 | 1573 | $stack[] = $this->columnQuote($column) . ' = REPLACE(' . $this->columnQuote($column) . ', ' . $map_key . 'a, ' . $map_key . 'b)'; 1574 | 1575 | $map[ $map_key . 'a' ] = [$old, PDO::PARAM_STR]; 1576 | $map[ $map_key . 'b' ] = [$new, PDO::PARAM_STR]; 1577 | } 1578 | } 1579 | } 1580 | 1581 | if (!empty($stack)) 1582 | { 1583 | return $this->exec('UPDATE ' . $this->tableQuote($table) . ' SET ' . implode(', ', $stack) . $this->whereClause($where, $map), $map); 1584 | } 1585 | 1586 | return false; 1587 | } 1588 | 1589 | public function get($table, $join = null, $columns = null, $where = null) 1590 | { 1591 | $map = []; 1592 | $result = []; 1593 | $column_map = []; 1594 | $current_stack = []; 1595 | 1596 | if ($where === null) 1597 | { 1598 | $column = $join; 1599 | unset($columns[ 'LIMIT' ]); 1600 | } 1601 | else 1602 | { 1603 | $column = $columns; 1604 | unset($where[ 'LIMIT' ]); 1605 | } 1606 | 1607 | $is_single = (is_string($column) && $column !== '*'); 1608 | 1609 | $query = $this->exec($this->selectContext($table, $map, $join, $columns, $where) . ' LIMIT 1', $map); 1610 | 1611 | if (!$this->statement) 1612 | { 1613 | return false; 1614 | } 1615 | 1616 | $data = $query->fetchAll(PDO::FETCH_ASSOC); 1617 | 1618 | if (isset($data[ 0 ])) 1619 | { 1620 | if ($column === '*') 1621 | { 1622 | return $data[ 0 ]; 1623 | } 1624 | 1625 | $this->columnMap($columns, $column_map, true); 1626 | 1627 | $this->dataMap($data[ 0 ], $columns, $column_map, $current_stack, true, $result); 1628 | 1629 | if ($is_single) 1630 | { 1631 | return $result[ 0 ][ $column_map[ $column ][ 0 ] ]; 1632 | } 1633 | 1634 | return $result[ 0 ]; 1635 | } 1636 | } 1637 | 1638 | public function has($table, $join, $where = null) 1639 | { 1640 | $map = []; 1641 | $column = null; 1642 | 1643 | if ($this->type === 'mssql') 1644 | { 1645 | $query = $this->exec($this->selectContext($table, $map, $join, $column, $where, Medoo::raw('TOP 1 1')), $map); 1646 | } 1647 | else 1648 | { 1649 | $query = $this->exec('SELECT EXISTS(' . $this->selectContext($table, $map, $join, $column, $where, 1) . ')', $map); 1650 | } 1651 | 1652 | if (!$this->statement) 1653 | { 1654 | return false; 1655 | } 1656 | 1657 | $result = $query->fetchColumn(); 1658 | 1659 | return $result === '1' || $result === 1 || $result === true; 1660 | } 1661 | 1662 | public function rand($table, $join = null, $columns = null, $where = null) 1663 | { 1664 | $type = $this->type; 1665 | 1666 | $order = 'RANDOM()'; 1667 | 1668 | if ($type === 'mysql') 1669 | { 1670 | $order = 'RAND()'; 1671 | } 1672 | elseif ($type === 'mssql') 1673 | { 1674 | $order = 'NEWID()'; 1675 | } 1676 | 1677 | $order_raw = $this->raw($order); 1678 | 1679 | if ($where === null) 1680 | { 1681 | if ($columns === null) 1682 | { 1683 | $columns = [ 1684 | 'ORDER' => $order_raw 1685 | ]; 1686 | } 1687 | else 1688 | { 1689 | $column = $join; 1690 | unset($columns[ 'ORDER' ]); 1691 | 1692 | $columns[ 'ORDER' ] = $order_raw; 1693 | } 1694 | } 1695 | else 1696 | { 1697 | unset($where[ 'ORDER' ]); 1698 | 1699 | $where[ 'ORDER' ] = $order_raw; 1700 | } 1701 | 1702 | return $this->select($table, $join, $columns, $where); 1703 | } 1704 | 1705 | private function aggregate($type, $table, $join = null, $column = null, $where = null) 1706 | { 1707 | $map = []; 1708 | 1709 | $query = $this->exec($this->selectContext($table, $map, $join, $column, $where, strtoupper($type)), $map); 1710 | 1711 | if (!$this->statement) 1712 | { 1713 | return false; 1714 | } 1715 | 1716 | $number = $query->fetchColumn(); 1717 | 1718 | return is_numeric($number) ? $number + 0 : $number; 1719 | } 1720 | 1721 | public function count($table, $join = null, $column = null, $where = null) 1722 | { 1723 | return $this->aggregate('count', $table, $join, $column, $where); 1724 | } 1725 | 1726 | public function avg($table, $join, $column = null, $where = null) 1727 | { 1728 | return $this->aggregate('avg', $table, $join, $column, $where); 1729 | } 1730 | 1731 | public function max($table, $join, $column = null, $where = null) 1732 | { 1733 | return $this->aggregate('max', $table, $join, $column, $where); 1734 | } 1735 | 1736 | public function min($table, $join, $column = null, $where = null) 1737 | { 1738 | return $this->aggregate('min', $table, $join, $column, $where); 1739 | } 1740 | 1741 | public function sum($table, $join, $column = null, $where = null) 1742 | { 1743 | return $this->aggregate('sum', $table, $join, $column, $where); 1744 | } 1745 | 1746 | public function action($actions) 1747 | { 1748 | if (is_callable($actions)) 1749 | { 1750 | $this->pdo->beginTransaction(); 1751 | 1752 | try { 1753 | $result = $actions($this); 1754 | 1755 | if ($result === false) 1756 | { 1757 | $this->pdo->rollBack(); 1758 | } 1759 | else 1760 | { 1761 | $this->pdo->commit(); 1762 | } 1763 | } 1764 | catch (Exception $e) { 1765 | $this->pdo->rollBack(); 1766 | 1767 | throw $e; 1768 | } 1769 | 1770 | return $result; 1771 | } 1772 | 1773 | return false; 1774 | } 1775 | 1776 | public function id() 1777 | { 1778 | if ($this->statement == null) 1779 | { 1780 | return null; 1781 | } 1782 | 1783 | $type = $this->type; 1784 | 1785 | if ($type === 'oracle') 1786 | { 1787 | return 0; 1788 | } 1789 | elseif ($type === 'pgsql') 1790 | { 1791 | return $this->pdo->query('SELECT LASTVAL()')->fetchColumn(); 1792 | } 1793 | 1794 | $lastId = $this->pdo->lastInsertId(); 1795 | 1796 | if ($lastId != "0" && $lastId != "") 1797 | { 1798 | return $lastId; 1799 | } 1800 | 1801 | return null; 1802 | } 1803 | 1804 | public function debug() 1805 | { 1806 | $this->debug_mode = true; 1807 | 1808 | return $this; 1809 | } 1810 | 1811 | public function error() 1812 | { 1813 | return $this->errorInfo; 1814 | } 1815 | 1816 | public function last() 1817 | { 1818 | $log = end($this->logs); 1819 | 1820 | return $this->generate($log[ 0 ], $log[ 1 ]); 1821 | } 1822 | 1823 | public function log() 1824 | { 1825 | return array_map(function ($log) 1826 | { 1827 | return $this->generate($log[ 0 ], $log[ 1 ]); 1828 | }, 1829 | $this->logs 1830 | ); 1831 | } 1832 | 1833 | public function info() 1834 | { 1835 | $output = [ 1836 | 'server' => 'SERVER_INFO', 1837 | 'driver' => 'DRIVER_NAME', 1838 | 'client' => 'CLIENT_VERSION', 1839 | 'version' => 'SERVER_VERSION', 1840 | 'connection' => 'CONNECTION_STATUS' 1841 | ]; 1842 | 1843 | foreach ($output as $key => $value) 1844 | { 1845 | $output[ $key ] = @$this->pdo->getAttribute(constant('PDO::ATTR_' . $value)); 1846 | } 1847 | 1848 | $output[ 'dsn' ] = $this->dsn; 1849 | 1850 | return $output; 1851 | } 1852 | } -------------------------------------------------------------------------------- /lib/3rd_party_helpers/jwt_helper.php: -------------------------------------------------------------------------------- 1 | 15 | * @author Anant Narayanan 16 | * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD 17 | * @link https://github.com/firebase/php-jwt 18 | */ 19 | class JWT 20 | { 21 | /** 22 | * Decodes a JWT string into a PHP object. 23 | * 24 | * @param string $jwt The JWT 25 | * @param string|null $key The secret key 26 | * @param bool $verify Don't skip verification process 27 | * 28 | * @return object The JWT's payload as a PHP object 29 | * @throws UnexpectedValueException Provided JWT was invalid 30 | * @throws DomainException Algorithm was not provided 31 | * 32 | * @uses jsonDecode 33 | * @uses urlsafeB64Decode 34 | */ 35 | public static function decode($jwt, $key = null, $verify = true) 36 | { 37 | $tks = explode('.', $jwt); 38 | if (count($tks) != 3) { 39 | throw new UnexpectedValueException('Wrong number of segments'); 40 | } 41 | list($headb64, $bodyb64, $cryptob64) = $tks; 42 | if (null === ($header = JWT::jsonDecode(JWT::urlsafeB64Decode($headb64)))) { 43 | throw new UnexpectedValueException('Invalid segment encoding'); 44 | } 45 | if (null === $payload = JWT::jsonDecode(JWT::urlsafeB64Decode($bodyb64))) { 46 | throw new UnexpectedValueException('Invalid segment encoding'); 47 | } 48 | $sig = JWT::urlsafeB64Decode($cryptob64); 49 | if ($verify) { 50 | if (empty($header->alg)) { 51 | throw new DomainException('Empty algorithm'); 52 | } 53 | if ($sig != JWT::sign("$headb64.$bodyb64", $key, $header->alg)) { 54 | throw new UnexpectedValueException('Signature verification failed'); 55 | } 56 | } 57 | return $payload; 58 | } 59 | /** 60 | * Converts and signs a PHP object or array into a JWT string. 61 | * 62 | * @param object|array $payload PHP object or array 63 | * @param string $key The secret key 64 | * @param string $algo The signing algorithm. Supported 65 | * algorithms are 'HS256', 'HS384' and 'HS512' 66 | * 67 | * @return string A signed JWT 68 | * @uses jsonEncode 69 | * @uses urlsafeB64Encode 70 | */ 71 | public static function encode($payload, $key, $algo = 'HS256') 72 | { 73 | $header = array('typ' => 'JWT', 'alg' => $algo); 74 | $segments = array(); 75 | $segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($header)); 76 | $segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($payload)); 77 | $signing_input = implode('.', $segments); 78 | $signature = JWT::sign($signing_input, $key, $algo); 79 | $segments[] = JWT::urlsafeB64Encode($signature); 80 | return implode('.', $segments); 81 | } 82 | /** 83 | * Sign a string with a given key and algorithm. 84 | * 85 | * @param string $msg The message to sign 86 | * @param string $key The secret key 87 | * @param string $method The signing algorithm. Supported 88 | * algorithms are 'HS256', 'HS384' and 'HS512' 89 | * 90 | * @return string An encrypted message 91 | * @throws DomainException Unsupported algorithm was specified 92 | */ 93 | public static function sign($msg, $key, $method = 'HS256') 94 | { 95 | $methods = array( 96 | 'HS256' => 'sha256', 97 | 'HS384' => 'sha384', 98 | 'HS512' => 'sha512', 99 | ); 100 | if (empty($methods[$method])) { 101 | throw new DomainException('Algorithm not supported'); 102 | } 103 | return hash_hmac($methods[$method], $msg, $key, true); 104 | } 105 | /** 106 | * Decode a JSON string into a PHP object. 107 | * 108 | * @param string $input JSON string 109 | * 110 | * @return object Object representation of JSON string 111 | * @throws DomainException Provided string was invalid JSON 112 | */ 113 | public static function jsonDecode($input) 114 | { 115 | $obj = json_decode($input); 116 | if (function_exists('json_last_error') && $errno = json_last_error()) { 117 | JWT::_handleJsonError($errno); 118 | } else if ($obj === null && $input !== 'null') { 119 | throw new DomainException('Null result with non-null input'); 120 | } 121 | return $obj; 122 | } 123 | /** 124 | * Encode a PHP object into a JSON string. 125 | * 126 | * @param object|array $input A PHP object or array 127 | * 128 | * @return string JSON representation of the PHP object or array 129 | * @throws DomainException Provided object could not be encoded to valid JSON 130 | */ 131 | public static function jsonEncode($input) 132 | { 133 | $json = json_encode($input); 134 | if (function_exists('json_last_error') && $errno = json_last_error()) { 135 | JWT::_handleJsonError($errno); 136 | } else if ($json === 'null' && $input !== null) { 137 | throw new DomainException('Null result with non-null input'); 138 | } 139 | return $json; 140 | } 141 | /** 142 | * Decode a string with URL-safe Base64. 143 | * 144 | * @param string $input A Base64 encoded string 145 | * 146 | * @return string A decoded string 147 | */ 148 | public static function urlsafeB64Decode($input) 149 | { 150 | $remainder = strlen($input) % 4; 151 | if ($remainder) { 152 | $padlen = 4 - $remainder; 153 | $input .= str_repeat('=', $padlen); 154 | } 155 | return base64_decode(strtr($input, '-_', '+/')); 156 | } 157 | /** 158 | * Encode a string with URL-safe Base64. 159 | * 160 | * @param string $input The string you want encoded 161 | * 162 | * @return string The base64 encode of what you passed in 163 | */ 164 | public static function urlsafeB64Encode($input) 165 | { 166 | return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); 167 | } 168 | /** 169 | * Helper method to create a JSON error. 170 | * 171 | * @param int $errno An error number from json_last_error() 172 | * 173 | * @return void 174 | */ 175 | private static function _handleJsonError($errno) 176 | { 177 | $messages = array( 178 | JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', 179 | JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', 180 | JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON' 181 | ); 182 | throw new DomainException( 183 | isset($messages[$errno]) 184 | ? $messages[$errno] 185 | : 'Unknown JSON error: ' . $errno 186 | ); 187 | } 188 | } -------------------------------------------------------------------------------- /lib/3rd_party_helpers/password_helper.php: -------------------------------------------------------------------------------- 1 | '$1;', 32 | '/(&#*\w+)[\x00-\x20]+;/u' => '$1;>', 33 | '/(&#x*[0-9A-F]+);*/iu' => '$1;', 34 | //any attribute starting with "on" or xml name space 35 | '#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu' => '$1>', 36 | //javascript: and VB script: protocols 37 | '#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu' => '$1=$2nojavascript...', 38 | '#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu' => '$1=$2novbscript...', 39 | '#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u' => '$1=$2nomozbinding...', 40 | // Only works in IE: 41 | '#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i' => '$1>', 42 | '#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu' => '$1>', 43 | // namespace elements 44 | '#]*+>#i' => '', 45 | //unwanted tags 46 | '#]*+>#i' => '' 47 | ); 48 | 49 | /** 50 | * @var array 51 | */ 52 | private $normal_patterns = array( 53 | '\'' => ''', 54 | '"' => '"', 55 | '&' => '&', 56 | '<' => '<', 57 | '>' => '>', 58 | //possible SQL injection remove from string with there is no ' 59 | 'SELECT * FROM' => '', 60 | 'SELECT(' => '', 61 | 'SLEEP(' => '', 62 | 'AND (' => '', 63 | ' AND' => '', 64 | '(CASE' => '' 65 | ); 66 | 67 | /** 68 | * xss_filter::filter_it() 69 | * 70 | * @access public 71 | * @param string $input 72 | * @return string 73 | */ 74 | public function filter_it($input){ 75 | $this->input = html_entity_decode($input, ENT_NOQUOTES, 'UTF-8'); 76 | $this->normal_replace(); 77 | $this->do_grep(); 78 | return $this->input; 79 | } 80 | 81 | /** 82 | * xss_filter::allow_http() 83 | * 84 | * @access public 85 | */ 86 | public function allow_http(){ 87 | $this->allow_http_value = true; 88 | } 89 | 90 | /** 91 | * xss_filter::disallow_http() 92 | * 93 | * @access public 94 | */ 95 | public function disallow_http(){ 96 | $this->allow_http_value = false; 97 | } 98 | 99 | /** 100 | * xss_filter::remove_get_parameters() 101 | * 102 | * @access public 103 | * @param $url string 104 | * @return string 105 | */ 106 | public function remove_get_parameters($url){ 107 | return preg_replace('/\?.*/', '', $url); 108 | } 109 | 110 | /** 111 | * xss_filter::normal_replace() 112 | * 113 | * @access private 114 | */ 115 | private function normal_replace(){ 116 | $this->input = str_replace(array('&', '<', '>'), array('&amp;', '&lt;', '&gt;'), $this->input); 117 | if($this->allow_http_value === false){ 118 | $this->input = str_replace(array('&', '%', 'script', 'http', 'localhost'), array('', '', '', '', ''), $this->input); 119 | } 120 | else 121 | { 122 | $this->input = str_replace(array('&', '%', 'script', 'localhost', '../'), array('', '', '', '', ''), $this->input); 123 | } 124 | foreach($this->normal_patterns as $pattern => $replacement){ 125 | $this->input = str_replace($pattern,$replacement,$this->input); 126 | } 127 | } 128 | 129 | /** 130 | * xss_filter::do_grep() 131 | * 132 | * @access private 133 | */ 134 | private function do_grep(){ 135 | foreach($this->preg_patterns as $pattern => $replacement){ 136 | $this->input = preg_replace($pattern,$replacement,$this->input); 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /lib/fsl_functions.php: -------------------------------------------------------------------------------- 1 | filter_it($string); 85 | return $string; 86 | } 87 | 88 | /* 89 | * fsl_session_set 90 | * 91 | * sets a new session value with optional timeout in seconds 92 | * also used to set any cookie variable. all data is set encrypted with global key 93 | * @name (string) Name of session 94 | * @value (string) value of session 95 | * @timeoue (string) optional timeout of session 96 | * @return true 97 | */ 98 | function fsl_session_set($name,$value,$timeout = NULL){ 99 | $_SESSION[$name] = fsl_encrypt($value); 100 | if(!empty($timeout)) $_SESSION[$name.'_timeout'] = $timeout + time(); 101 | 102 | return true; 103 | } 104 | 105 | /* 106 | * fsl_session_check 107 | * 108 | * gets a session value and optionally resets a new value or timeout 109 | * if session doesn't exist or timed out, then returns false, else returns value of session 110 | * @name (string) Name of session 111 | * @value (string) value of session 112 | * @timeoue (string) optional timeout of session 113 | * @return true 114 | */ 115 | function fsl_session_check($name,$value = NULL,$timeout = NULL){ 116 | if ((empty($_SESSION[$name] )) || ( (!empty( $_SESSION[$name.'_timeout'])) && (time() > $_SESSION[$name.'_timeout'])) ) 117 | { 118 | fsl_session_kill($name); //run kill session command 119 | return false; 120 | } 121 | else 122 | { 123 | if(!empty($value)) $_SESSION[$name] = fsl_encrypt($value); 124 | if(!empty($timeout)) $_SESSION[$name.'_timeout'] = $timeout; 125 | } 126 | 127 | return fsl_decrypt($_SESSION[$name] ); 128 | } 129 | 130 | /* 131 | * fsl_session_kill 132 | * 133 | * kills a session and associated timeout 134 | * @name (string) Name of session 135 | * returns true 136 | */ 137 | 138 | function fsl_session_kill($name){ 139 | unset($_SESSION[''.$name.'']); 140 | unset($_SESSION[''.$name.'_timeout']); 141 | session_destroy(); 142 | return true; 143 | } 144 | 145 | /* 146 | * 147 | * fsl_jwt_encode 148 | * 149 | * creates a JWT Token 150 | * 151 | * @array (array) Array to be encoded in JWT 152 | * @key (string) OPTIONAL encryption key to use. If not provided default 153 | * key specified with option('global_encryption_key', 'setyourkeyhere') config 154 | * @return (string) 155 | */ 156 | function fsl_jwt_encode($array,$key) 157 | { 158 | return JWT::encode($array, $key); 159 | } 160 | 161 | /* 162 | * 163 | * fsl_jwt_decode 164 | * 165 | * Decodes and validates a JWT Token and key combination 166 | * 167 | * @token (string) JWT to be decoded 168 | * @key (string) OPTIONAL encryption key to use. If not provided default 169 | * key specified with option('global_encryption_key', 'setyourkeyhere') config 170 | * @return (array) or error if validations fails 171 | */ 172 | function fsl_jwt_decode($token,$key) 173 | { 174 | return JWT::decode($token, $key); 175 | } 176 | 177 | /* 178 | * 179 | * get_tiny_url 180 | * 181 | * Generates a Tiny URL 182 | * 183 | * @url (string) url to be shortened 184 | * @return (string) 185 | */ 186 | function fsl_get_tiny_url($url) { 187 | $ch = curl_init(); 188 | $timeout = 5; 189 | curl_setopt($ch,CURLOPT_URL,'https://tinyurl.com/api-create.php?url='.$url); 190 | curl_setopt($ch,CURLOPT_RETURNTRANSFER,1); 191 | curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,$timeout); 192 | $data = curl_exec($ch); 193 | curl_close($ch); 194 | 195 | return preg_replace("/^http:/i", "https:", $data); 196 | } 197 | 198 | 199 | /* 200 | * fsl_gauth_check 201 | * 202 | * checks if glogin session is set to 1 if not returns false 203 | * 204 | * @string (string) String to be decrypted 205 | * @key (string) OPTIONAL encryption key to use. If not provided default 206 | * key specified with option('global_encryption_key', 'setyourkeyhere') config 207 | * @return (string) 208 | */ 209 | 210 | function fsl_gauth_check(){ 211 | if ($_SESSION['glogin'] == 1) 212 | { 213 | return true; 214 | } 215 | else 216 | { 217 | return false; 218 | } 219 | } 220 | 221 | 222 | /* 223 | * fsl_gauth_getauthurl 224 | * 225 | * generates the auth url to use on google login button 226 | * 227 | * @string (string) String to be decrypted 228 | * @key (string) OPTIONAL encryption key to use. If not provided default 229 | * key specified with option('global_encryption_key', 'setyourkeyhere') config 230 | * @return (string) 231 | */ 232 | function fsl_gauth_getauthurl() 233 | { 234 | 235 | $gClient = new Google_Client(); 236 | $gClient->setApplicationName('Login'); 237 | $gClient->setClientId(option('clientId')); 238 | $gClient->setClientSecret(option('clientSecret')); 239 | $gClient->setRedirectUri(option('redirectURL')); 240 | // $gClient->setHd(option('hd')); 241 | $google_oauthV2 = new Google_Oauth2Service($gClient); 242 | $authUrl = $gClient->createAuthUrl(); 243 | return $authUrl; 244 | } 245 | 246 | /* 247 | * fsl_gauth_gettoken 248 | * 249 | * verify token from gauth is valid. If it is, returns array of google parameters 250 | * 251 | * @string (string) String to be decrypted 252 | * @key (string) OPTIONAL encryption key to use. If not provided default 253 | * key specified with option('global_encryption_key', 'setyourkeyhere') config 254 | * @return (string) 255 | */ 256 | function fsl_gauth_gettoken() 257 | { 258 | $gClient = new Google_Client(); 259 | $gClient->setApplicationName('Login'); 260 | $gClient->setClientId(option('clientId')); 261 | $gClient->setClientSecret(option('clientSecret')); 262 | $gClient->setRedirectUri(option('redirectURL')); 263 | // $gClient->setHd(option('hd')); 264 | $google_oauthV2 = new Google_Oauth2Service($gClient); 265 | 266 | //google auth first 267 | if(isset($_GET['code'])){ 268 | $gClient->authenticate($_GET['code']); 269 | $_SESSION['token'] = $gClient->getAccessToken(); 270 | 271 | header('Location: ' . filter_var($redirectURL, FILTER_SANITIZE_URL)); 272 | } 273 | 274 | if (isset($_SESSION['token'])) { 275 | $gClient->setAccessToken($_SESSION['token']); 276 | } 277 | //Call Google API 278 | 279 | if ($gClient->getAccessToken()) { 280 | //Get user profile data from google 281 | $gpUserProfile = $google_oauthV2->userinfo->get(); 282 | 283 | /* $gpUserData = array( 284 | 'oauth_provider'=> 'google', 285 | 'oauth_uid' => $gpUserProfile['id'], 286 | 'first_name' => $gpUserProfile['given_name'], 287 | 'last_name' => $gpUserProfile['family_name'], 288 | 'email' => $gpUserProfile['email'], 289 | 'gender' => $gpUserProfile['gender'], 290 | 'locale' => $gpUserProfile['locale'], 291 | 'picture' => $gpUserProfile['picture'], 292 | 'link' => $gpUserProfile['link'] 293 | ); */ 294 | // $userData = $user->checkUser($gpUserData); 295 | //$output = $gpUserProfile['given_name']; 296 | //$uemail = $gpUserProfile['email']; 297 | $_SESSION['glogin'] = 1; 298 | 299 | $uemail = substr(strrchr($uemail, "@"), 1); 300 | if ($uemail == "konghq.com"){ 301 | header('Location: ' . option('base_uri') . 'home/'); 302 | }else { 303 | header('Location: ' . option('base_uri') . 'logout/'); 304 | } 305 | return $gpUserData; 306 | 307 | } else { 308 | 309 | header('Location: ' . option('base_uri') . 'gauth/login/'); 310 | } 311 | } 312 | 313 | /* 314 | * fsl_gauth_logout 315 | * 316 | * unsets google token and returns to base URL or URL of choice 317 | * 318 | * @string (string) String to be decrypted 319 | * @key (string) OPTIONAL encryption key to use. If not provided default 320 | * key specified with option('global_encryption_key', 'setyourkeyhere') config 321 | * @return (string) 322 | */ 323 | 324 | /* 325 | * 326 | * fsl_hash_create 327 | * 328 | * creates a one way sha256 hash. Good for storing passwords, keys, and other data 329 | * elements where you do not need to unencrypt 330 | * 331 | * @string (string) data to be hashed 332 | * @return (string) 333 | */ 334 | function fsl_hash_create($string) 335 | { 336 | return Password::create_hash($string); 337 | } 338 | 339 | /* 340 | * 341 | * fsl_hash_validate 342 | * 343 | * verify that a string matches a stored hashed version of the string 344 | * 345 | * @string (string) data to be validated 346 | * @good_hash (string) hash to be compared against 347 | * @return (boolean) 348 | */ 349 | function fsl_hash_validate($string,$good_hash) 350 | { 351 | return Password::validate_password($string, $good_hash); 352 | } 353 | 354 | 355 | /* 356 | * 357 | * fsl_curl 358 | * 359 | * make an external http call. helpful when calling external api's 360 | * 361 | * @url: url of api 362 | * @method: action of request. Options include GET POST PUT DELETE 363 | * @datatype: expected data either XML or JSON supported. Otherwise defaults to * 364 | * @urlparams: url parameters (query string) 365 | * @postdata: array of data to submit 366 | * @authtype: authentication if needed BASIC or TOKEN (bearer token) 367 | * @$authuser: basic auth user 368 | * @$authpassword: basic auth password 369 | * @$authtoken: bearer token 370 | * @$customheader: ARRAY of custom headers 371 | * output: return array(http response code, curl info, response); 372 | */ 373 | 374 | function fsl_curl($url, $method = "GET", $datatype = NULL, $urlparams = NULL, $postdata = NULL, $authtype = NULL, $authuser = NULL, $authpassword = NULL, $authtoken = NULL, $customheader = NULL ) { 375 | ini_set("default_socket_timeout", 10); 376 | if ($urlparams != NULL) { 377 | $url .= '?' . $urlparams; 378 | } 379 | 380 | 381 | //set user agent 382 | $headers = array('User-Agent: Fresh Squeezed Limonade (https://github.com/yesinteractive/fsl)'); 383 | 384 | $ch = curl_init(); 385 | curl_setopt($ch, CURLOPT_URL, $url); 386 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 387 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 'FALSE'); 388 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 'FALSE'); 389 | curl_setopt($ch, CURLOPT_TIMEOUT, 45); //timeout in seconds 390 | 391 | //data type 392 | if ($datatype == "XML") { 393 | array_push($headers,'Content-Type: application/xml'); 394 | }else if ($datatype == "JSON") { 395 | array_push($headers,'Content-Type: application/json'); 396 | } 397 | else if ($datatype == "FORM") { 398 | array_push($headers,'Content-Type: application/x-www-form-urlencoded'); 399 | }else { 400 | array_push($headers,'Content-Type: */*'); 401 | } 402 | 403 | //method 404 | if ($method == "POST") { 405 | //need to post the values? 406 | curl_setopt($ch, CURLOPT_POST, true); 407 | //fields which will be posted. 408 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata); 409 | } else if ($method == "PUT") { 410 | curl_setopt($ch, CURLOPT_PUT, true); 411 | } else if ($method == "DELETE") { 412 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); 413 | } else{ 414 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET"); 415 | } 416 | 417 | //auth 418 | if ($authtype == "BASIC") { 419 | curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 420 | curl_setopt($ch, CURLOPT_USERPWD, "$authuser:$authpassword"); 421 | } else if ($authtype == "TOKEN") { 422 | array_push($headers,'Authorization: Bearer ' . $authtoken); 423 | } else{ 424 | //no auth 425 | } 426 | 427 | //merge headeer arrays if customheader set and set headers 428 | if ($customheader == NULL){ 429 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 430 | } else { 431 | curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge($headers,$customheader)); 432 | } 433 | 434 | $output = curl_exec($ch); 435 | $info = curl_getinfo($ch); 436 | $code = $info['http_code']; 437 | curl_close($ch); 438 | 439 | return $ret = array($code, $info, $output); 440 | } 441 | 442 | 443 | ?> -------------------------------------------------------------------------------- /lib/limonade/public/css/screen.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesinteractive/kong-map/a2847d32445e67cce3e05fd621f8c571fe56bdf1/lib/limonade/public/css/screen.css -------------------------------------------------------------------------------- /lib/limonade/public/img/404.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesinteractive/kong-map/a2847d32445e67cce3e05fd621f8c571fe56bdf1/lib/limonade/public/img/404.gif -------------------------------------------------------------------------------- /lib/limonade/public/img/fsl_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesinteractive/kong-map/a2847d32445e67cce3e05fd621f8c571fe56bdf1/lib/limonade/public/img/fsl_logo.png -------------------------------------------------------------------------------- /lib/limonade/views/_debug.html.php: -------------------------------------------------------------------------------- 1 | ENV_PRODUCTION && option('debug')): ?> 2 | 3 |

[] 4 | (in line ) 5 |

6 | 7 | 8 | 9 | 10 |

Debug arguments

11 |
12 | 13 | 14 |

Options

15 |
16 |

[ ↑ ]

17 | 18 |

Environment

19 |
20 |

[ ↑ ]

21 | 22 |

Backtrace

23 |
24 |

[ ↑ ]

25 | 26 |
27 | 28 | 29 | Debug arguments | 30 | 31 | Options | 32 | Environment | 33 | Backtrace | 34 | [ ↑ ] 35 |
36 | 37 | -------------------------------------------------------------------------------- /lib/limonade/views/_notices.html.php: -------------------------------------------------------------------------------- 1 | 2 |
3 |

→ Notices and warnings

4 |
5 | 6 |
[]
7 |
8 | in 9 | line 10 |
11 | 12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /lib/limonade/views/default_layout.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FSL Micro Framework for PHP 6 | 7 | 8 | 9 | 10 | 15 | 16 |
17 | 18 |
19 | 20 |
21 |
22 |
23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /lib/limonade/views/error.html.php: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | 6 | -------------------------------------------------------------------------------- /public/banner-fsl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesinteractive/kong-map/a2847d32445e67cce3e05fd621f8c571fe56bdf1/public/banner-fsl.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesinteractive/kong-map/a2847d32445e67cce3e05fd621f8c571fe56bdf1/public/favicon.ico -------------------------------------------------------------------------------- /public/fsl.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesinteractive/kong-map/a2847d32445e67cce3e05fd621f8c571fe56bdf1/public/fsl.jpeg -------------------------------------------------------------------------------- /public/fsl_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesinteractive/kong-map/a2847d32445e67cce3e05fd621f8c571fe56bdf1/public/fsl_logo.png -------------------------------------------------------------------------------- /public/kongmap2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesinteractive/kong-map/a2847d32445e67cce3e05fd621f8c571fe56bdf1/public/kongmap2.png -------------------------------------------------------------------------------- /public/kongmap3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesinteractive/kong-map/a2847d32445e67cce3e05fd621f8c571fe56bdf1/public/kongmap3.png -------------------------------------------------------------------------------- /public/launchpage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesinteractive/kong-map/a2847d32445e67cce3e05fd621f8c571fe56bdf1/public/launchpage.png -------------------------------------------------------------------------------- /public/soda_glass.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesinteractive/kong-map/a2847d32445e67cce3e05fd621f8c571fe56bdf1/public/soda_glass.jpg -------------------------------------------------------------------------------- /public/soda_glass.thb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesinteractive/kong-map/a2847d32445e67cce3e05fd621f8c571fe56bdf1/public/soda_glass.thb.jpg -------------------------------------------------------------------------------- /screenshots/kongmap-deck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesinteractive/kong-map/a2847d32445e67cce3e05fd621f8c571fe56bdf1/screenshots/kongmap-deck.png -------------------------------------------------------------------------------- /screenshots/kongmap-endpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesinteractive/kong-map/a2847d32445e67cce3e05fd621f8c571fe56bdf1/screenshots/kongmap-endpoint.png -------------------------------------------------------------------------------- /screenshots/kongmap-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesinteractive/kong-map/a2847d32445e67cce3e05fd621f8c571fe56bdf1/screenshots/kongmap-home.png -------------------------------------------------------------------------------- /views/fsl_default_layout.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | KongMap - API Gateway Visualizer 12 | 13 | 14 | 15 | 16 | 24 | 25 | 26 | 54 | 55 | 56 | 57 | 71 | 72 | 73 | 74 |
75 | 76 | 77 | 78 | 79 |
80 |
81 | Visit Project on GitHub for documentation and to submit questions and issues. 82 |
83 | The Kong trademark, product name and logo are property of Kong Inc. 84 | 85 |
86 |
87 | 88 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /views/mapview.php: -------------------------------------------------------------------------------- 1 |