Compare commits

..
83 Commits
Author SHA1 Message Date
Dave Page 37bde4aaf4 Credit reporter who provided test script. 2019-05-29 16:34:04 -04:00
Dave Page 1f76d207f2 Minor phrasing fix. 2019-05-29 16:32:35 -04:00
Dave Page 8eab534c95 Fix typo 2019-05-29 16:27:56 -04:00
Dave Page 0eb3008426 Tidy up keyboard shortcut docs. 2019-05-29 16:24:29 -04:00
Aditya Toshniwal dd187b9122 Prevent Alertify from inadvertently opening help pages. 2019-05-29 13:46:39 -04:00
Aditya Toshniwal 15252b0a26 Added Help button on all master password dialogs. 2019-05-29 13:07:50 +05:30
Akshay Joshi aa95ff9f57 Fix encoding issue for background process, it is regression of Master Password. Fixes #4303 2019-05-29 12:50:48 +05:30
Fred Escallier dad0d690db Update French translation. 2019-05-29 12:25:37 +05:30
Dave Page 2fa849dc26 Also need openssl-dev 2019-05-28 18:11:42 -04:00
Dave Page f340cd044b build-deps doesn't exist any more. 2019-05-28 18:09:06 -04:00
Dave Page 991f667f9f Argh, they are Alpine packages, not Python. 2019-05-28 18:01:56 -04:00
Dave Page 307637b969 Looks like we also need build tools for the cryptography module. 2019-05-28 17:56:54 -04:00
Dave Page 91595593d3 Doc builds now need the cryptography module. 2019-05-28 17:50:11 -04:00
Identifier Anonymous 12186a0fd6 Update Japanese translation. 2019-05-28 17:11:27 +05:30
Ashesh Vashi 9fdf68d559 Fix fatal error on launch of pgAdmin4. Fixes #4298 2019-05-28 17:05:54 +05:30
Akshay Joshi 8851cb0704 Update version for release 2019-05-28 12:09:33 +05:30
Akshay Joshi a936f708de Update message catalogs. 2019-05-28 12:06:44 +05:30
Aditya Toshniwal dfa892d2a2 1. Added Master Password to increase the security of saved passwords. Fixes #4184
2. In server(web) mode, update all the saved server credentials when user password is changed. Fixes #3377
2019-05-28 12:00:18 +05:30
Khushboo Vashi 6f0eafb223 Fixed CSRF security vulnerability issue. per Alvin Lindstam. Fixes #4217
Initial patch by: Khushboo Vashi
Modified by: Ashesh Vashi and Murtuza Zabuawala
2019-05-28 10:59:51 +05:30
Akshay Joshi 90a45557b9 1. Fix syntax error when adding more than one column to the existing table. Fixes #4162
2. Fix syntax error when creating a table with a serial column. Fixes #4284
2019-05-27 13:09:11 +05:30
Akshay Joshi 2790d01c31 Update message catalogs. 2019-05-24 17:26:26 +05:30
Dave Page 4d23bb24a5 Add a missing fix. 2019-05-24 09:36:33 +01:00
Aditya Toshniwal 361f7077fd Update the UI logo. Artwork by Chethana Kumar. Fixes #4208 2019-05-23 16:39:10 +01:00
Usman Muzaffar dcd163ac02 Feature test stabilisation. 2019-05-23 09:31:52 +01:00
Aditya Toshniwal ee37be56f9 Fix handling of JSON in the Query Tool with NULL elements. Fixes #4263 2019-05-23 08:53:29 +01:00
Dave Page f1ef7b0346 Validate permissions on the session directory itself, not it's parent, per Felix Pusch. Fixes #4276 2019-05-21 12:14:36 +01:00
Dave Page 0ee50ac248 Clarify wording for the NO INHERIT option on constraints, per Michel Feinstein. Fixes #4275 2019-05-21 11:51:19 +01:00
Dave Page ea4dbd31e8 Don't try to set permissions on Windows. It won't work anyway. Fixes #4276 2019-05-20 13:53:08 +01:00
Murtuza Zabuawala 155768a2d7 Prevent Backgrid Password cells from losing focus if the browser opens an autocomplete list. Fixes #4278 2019-05-20 13:34:47 +01:00
Dave Page da8da7bb49 Relax the permission check on the directory containing the config database, as it may fail in some environments such as OpenShift. Fixes #4276 2019-05-20 09:46:02 +01:00
Murtuza Zabuawala 5786c17569 Fix navigation of switch cells in grids. Fixes #4269 2019-05-17 16:27:14 +01:00
Dave Page 941dea99f5 Fix typo. 2019-05-17 09:04:04 +01:00
Sander Eikelenboom ab5f35da8d Fixed error on displaying table properties of a table partitioned by list having a default partition. Fixes #4262 2019-05-15 18:36:16 +05:30
Aditya Toshniwal db4bb22cc6 Ensure that element should get highlighted when they get focus on using Tab key. Fixes #4245 2019-05-15 17:42:07 +05:30
Aditya Toshniwal 2cd58efcdd 1) Fixed Tab key navigation for Maintenance dialog. Fixes #4227
2) Fix Tab key issue for Toggle switch controls and button on the dialog footer in Safari browser. Fixes #4244
2019-05-15 16:37:06 +05:30
Aditya Toshniwal ed8d1cde00 Fix accessibility issue for menu navigation. Fixes #4194 2019-05-15 15:38:33 +05:30
Akshay Joshi b7121bb2d2 1) Make folder name pluralise inside 'templates' folder and fix naming inconsistency.
2) Remove unwanted functions "module_js()" from the code.
3) Change naming convention for 'Roles' module.
2019-05-15 15:11:54 +05:30
Dave Page 3364350b01 Fix the check target. 2019-05-15 10:03:29 +01:00
Dave Page 4b421550d3 Stop using application/x-javascript as a mime type and use the RFC-compliant application/javascript instead. Fixes #4261 2019-05-15 09:44:57 +01:00
Dave Page 020d9bb018 Clarify when the container will load server definitions. 2019-05-14 09:27:47 +01:00
Murtuza Zabuawala eb189844e7 Fixed console error when subnode control is used in panels. Fixes #4246 2019-05-10 11:24:15 +05:30
Akshay Joshi 20c42d878b Updated jQuery version as they have fixed some vulnerability issue. 2019-05-03 16:47:35 +05:30
Aditya Toshniwal 036bd66638 Fix file browser path issue which occurs when client is on Windows and server is on Mac/Linux. Fixes #4164 2019-05-03 11:58:06 +05:30
Dave Page f02f91cbf1 Ensure popper.js is installed when needed. Fixes #4219 2019-04-30 13:57:10 +01:00
Neel Patel 34b59da526 Properly assign dropdownParent in Select2 controls. Fixes #4218 2019-04-30 12:57:17 +01:00
Fred Escallier 277bf85645 Update French translation. 2019-04-30 11:47:17 +05:30
Akshay Joshi 991fe3d797 Update version for release. 2019-04-30 10:49:49 +05:30
Identifier Anonymous 4009ac3108 Update Japanese translation 2019-04-29 09:21:55 +01:00
Akshay Joshi 92e6a47713 Update message catalogs. 2019-04-26 17:39:45 +05:30
Dave Page e53c2b1a5d Ensure the treeview shows all sequences except those used to implement IDENTITY columns (which can be edited as part of the column). Show all if Show System Objects is enabled. Fixes #4154 2019-04-26 12:36:56 +01:00
Dave Page e536408ce2 Don't try to install Chromedriver using the chromedriver_installer module as it no longer works and doesn't seem to be supported. 2019-04-25 09:41:40 +01:00
Dave Page f74448a1b2 Add a script for installing the required Chromedriver version. 2019-04-25 09:38:52 +01:00
Akshay Joshi 9b5a6e2bb7 Prevent duplicate columns being included in reverse engineered SQL for tables. Fixes #4163 2019-04-24 11:35:20 +01:00
Aditya Toshniwal 3fef078ec8 Remove some unecessary React libraries. 2019-04-24 09:29:01 +01:00
Khushboo Vashi 74af8976c9 Ensure that newly added row in backgrid should be visible. 2019-04-23 17:32:00 +05:30
Khushboo Vashi ecded18333 Fix an issue where 'GRANT UPDATE' sql should be displayed for default sequence privileges. Fixes #4087 2019-04-22 12:24:47 +05:30
Khushboo Vashi 5452bff49e Fixed 'Increment value cannot be empty' error for existing tables. Fixes #4160 2019-04-22 12:04:49 +05:30
Aditya Toshniwal 05ae330030 Improvement for 'beforeunload' event, bind the event only from where it is required. 2019-04-22 11:30:33 +05:30
Dave Page 94e1820ccd Fix skip on Windows. 2019-04-18 11:18:06 +01:00
Aditya Toshniwal 49b318c39e Ensure that confirmation dialog should be popped up before reload of query tool or debugger
if it is opened in a new browser tab. Fixes #4101
2019-04-18 15:39:36 +05:30
Dave Page 4ac064de42 Urgh. Typo. 2019-04-18 09:13:55 +01:00
Dave Page 2ef71cf749 Skip the permissions test on Windows which doesn't support Unix style file permissions. 2019-04-18 09:13:00 +01:00
Aditya Toshniwal d96f453e7f Fixed 'Location cannot be empty' error when open Tablespace properties. Fixes #4134 2019-04-18 12:31:35 +05:30
Khushboo Vashi 0b47d1dbf6 1) Fix an issue where Deleting N number of rows makes first N number of rows disable. Fixes #3605
2) Ensure that on clicking Delete button should not delete rows immediately from the database server,
   it should be deleted when Save button will be clicked. Fixes #2392
2019-04-18 12:09:35 +05:30
Dave Page d728ee7436 Ensure sanity of the permissions on the storage and session directories and the config database. Fixes #4182 2019-04-17 16:57:34 +01:00
Dave Page 1a192109d6 Ensure we create a DMG file that's compatible with macOS 10.10+ 2019-04-15 12:12:46 +01:00
Aditya Toshniwal 024cfd2fab 1) Ensure that JSON strings as comments should be added properly for all the objects. Fixes #3582
2) Fixed alignment issue of columns in definition section of Index node. Fixes #4121
2019-04-12 15:07:07 +05:30
Murtuza Zabuawala a25bb2b7db Add tests for the CSV download. 2019-04-11 17:15:01 +01:00
Fred Escallier fd3337024f Update French translation. 2019-04-11 13:51:40 +01:00
Dave Page ded446fe8a Depend on psycopg2-binary in the Python wheel, rather than psycopg2. Fixes #4165 2019-04-11 10:08:44 +01:00
Khushboo Vashi a9d964b5ca 1) Added support for Default Partition. Fixes #3938
2) Ensure that record should be add/edited for root partition table with primary keys. Fixes #4104
2019-04-11 13:25:24 +05:30
Akshay Joshi 9c3925e448 Ensure that parameters of procedures for EPAS server 10 and below should be set/reset properly. Fixes #4161 2019-04-10 13:22:52 +05:30
Murtuza Zabuawala 6016a5ee98 Fix an issue where the dropdown becomes misaligned/displaced. Fixes #4138 2019-04-09 12:09:25 +05:30
Akshay Joshi 490999f528 Updated release notes 2019-04-08 18:33:20 +05:30
Akshay Joshi ceb0e39e14 Update version for release 2019-04-08 18:31:51 +05:30
Akshay Joshi 3bed332439 Update message catalogs. 2019-04-08 18:24:36 +05:30
Khushboo Vashi c21ea3c342 Ensure that pgAdmin4 should work properly with psycopg2 v2.8. Fixes #4143 2019-04-08 17:49:50 +05:30
Murtuza Zabuawala 84ecffa4d9 Added recommended ESLinter checks. Fixes #4142 2019-04-05 13:07:43 +05:30
Akshay Joshi 9bff6c7157 Fixed pep8 issue 2019-04-05 12:53:50 +05:30
Usman Muzaffar 5c4c09f237 Fixed feature test issues 2019-04-05 12:25:03 +05:30
Dave Page f706f7ed0c Relabel the Save button on the datagrid text editor to avoid confusion with the actual Save button that updates the database. Fixes #4131 2019-04-02 17:20:43 +01:00
Identifier Anonymous fb12b2e2fc Updated Japanese tanslation. 2019-04-02 14:41:05 +01:00
Fred Escallier badbbade08 Updated French tanslation. 2019-04-02 14:38:47 +01:00
909 changed files with 26995 additions and 71203 deletions

No files matched your search

+2 -2
View File
@@ -31,8 +31,8 @@ bundle-dev:
linter:
cd web && yarn run linter
check: install-node bundle linter
cd web && pycodestyle --config=.pycodestyle . && yarn run karma start --single-run && python regression/runtests.py
check: install-node bundle linter check-pep8
cd web && yarn run karma start --single-run && python regression/runtests.py
check-pep8:
pycodestyle --config=.pycodestyle docs/
+3 -2
View File
@@ -33,8 +33,9 @@ Click the *Definition* tab to continue.
Use the fields in the *Definition* tab to define the check constraint:
* Provide the expression that a row must satisfy in the *Check* field.
* Move the *No Inherit?* switch to the *Yes* position to specify this constraint
is automatically inherited by a table's children. The default is *No*.
* Move the *No Inherit?* switch to the *Yes* position to specify that this
constraint is not automatically inherited by a table's children. The default
is *No*, meaning that the constraint will be inherited by any children.
* Move the *Don't validate?* switch to the *No* position to skip validation of
existing data; the constraint may not hold for all rows in the table. The
default is *Yes*.
+7
View File
@@ -24,6 +24,13 @@ dialog, right-click on the *Servers* node of the tree control, and select
server_dialog
A master password is required to secure and later unlock saved server passwords.
It is set by the user and can be disabled using config.
.. toctree::
master_password
After defining a server connection, right-click on the server name, and select
*Connect to server* to authenticate with the server, and start using pgAdmin to
manage objects that reside on the server.
+3 -2
View File
@@ -88,7 +88,6 @@ instances:
This is the working directory in which pgAdmin stores session data, user files,
configuration files, and it's configuration database. Mapping this directory
onto the host machine gives you an easy way to maintain configuration between
invocations of the container.
*/pgadmin4/config_local.py*
@@ -101,7 +100,9 @@ required.
If this file is mapped, server definitions found in it will be loaded at launch
time. This allows connection information to be pre-loaded into the instance of
pgAdmin in the container.
pgAdmin in the container. Note that server definitions are only loaded on first
launch, i.e. when the configuration database is created, and not on subsequent
launches using the same configuration database.
*/certs/server.cert*
Executable → Regular
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Executable → Regular
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 82 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

After

Width:  |  Height:  |  Size: 247 KiB

Executable → Regular
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Executable → Regular
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Executable → Regular
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 111 KiB

+63 -63
View File
@@ -73,49 +73,49 @@ When using the syntax-highlighting SQL editors, the following shortcuts are avai
:class: longtable
:widths: 2 2 3
+--------------------------+------------------+-------------------------------------+
| Shortcut (Windows/Linux) | Shortcut (Mac) | Function |
+==========================+==================+=====================================+
| Alt+Left | Option+Left | Move to the beginning of the line |
+--------------------------+------------------+-------------------------------------+
| Alt+Right | Option+Right | Move to the end of the line |
+--------------------------+------------------+-------------------------------------+
| Ctrl+Alt+Left | Cmd+Option+Left | Move left one word |
+--------------------------+------------------+-------------------------------------+
| Ctrl+Alt+Right | Cmd+Option+Right | Move right one word |
+--------------------------+------------------+-------------------------------------+
| Ctrl+/ | Cmd+/ | Comment selected code (Inline) |
+--------------------------+------------------+-------------------------------------+
| Ctrl+. | Cmd+. | Uncomment selected code (Inline) |
+--------------------------+------------------+-------------------------------------+
| Ctrl+Shift+/ | Cmd+Shift+/ | Comment/Uncomment code (Block) |
+--------------------------+------------------+-------------------------------------+
| Ctrl+A | Cmd+A | Select all |
+--------------------------+------------------+-------------------------------------+
| Ctrl+C | Cmd+C | Copy selected text to the clipboard |
+--------------------------+------------------+-------------------------------------+
| Ctrl+R | Cmd+R | Redo last edit un-done |
+--------------------------+------------------+-------------------------------------+
| Ctrl+V | Cmd+V | Paste text from the clipboard |
+--------------------------+------------------+-------------------------------------+
| Ctrl+Z | Cmd+Z | Undo last edit |
+--------------------------+------------------+-------------------------------------+
| Tab | Tab | Indent selected text |
+--------------------------+------------------+-------------------------------------+
| Shift+Tab | Shift+Tab | Un-indent selected text |
+--------------------------+------------------+-------------------------------------+
| Alt+G | Alt+G | Jump (to line:column) |
+--------------------------+------------------+-------------------------------------+
| Ctrl+Space | Ctrl+Space | Auto-complete |
+--------------------------+------------------+-------------------------------------+
| Ctrl+F | Cmd+F | Find |
+--------------------------+------------------+-------------------------------------+
| Ctrl+G | Cmd+G | Find next |
+--------------------------+------------------+-------------------------------------+
| Ctrl+Shift+G | Cmd+Shift+G | Find previous |
+--------------------------+------------------+-------------------------------------+
| Ctrl+Shift+F | Cmd+Shift+F | Replace |
+--------------------------+------------------+-------------------------------------+
+--------------------------+----------------------+-------------------------------------+
| Shortcut (Windows/Linux) | Shortcut (Mac) | Function |
+==========================+======================+=====================================+
| Alt + Left | Option + Left | Move to the beginning of the line |
+--------------------------+----------------------+-------------------------------------+
| Alt + Right | Option + Right | Move to the end of the line |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + Alt + Left | Cmd + Option + Left | Move left one word |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + Alt + Right | Cmd + Option + Right | Move right one word |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + / | Cmd + / | Comment selected code (Inline) |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + . | Cmd + . | Uncomment selected code (Inline) |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + Shift + / | Cmd + Shift + / | Comment/Uncomment code (Block) |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + a | Cmd + a | Select all |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + c | Cmd + c | Copy selected text to the clipboard |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + r | Cmd + r | Redo last edit un-done |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + v | Cmd + v | Paste text from the clipboard |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + z | Cmd + z | Undo last edit |
+--------------------------+----------------------+-------------------------------------+
| Tab | Tab | Indent selected text |
+--------------------------+----------------------+-------------------------------------+
| Shift + Tab | Shift + Tab | Un-indent selected text |
+--------------------------+----------------------+-------------------------------------+
| Alt + g | Option + g | Jump (to line:column) |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + Space | Ctrl + Space | Auto-complete |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + f | Cmd + f | Find |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + g | Cmd + g | Find next |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + Shift + g | Cmd + Shift + g | Find previous |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + Shift + f | Cmd + Shift + f | Replace |
+--------------------------+----------------------+-------------------------------------+
Query Tool
**********
@@ -133,7 +133,7 @@ When using the Query Tool, the following shortcuts are available:
+--------------------------+--------------------+-----------------------------------+
| F7 | F7 | EXPLAIN query |
+--------------------------+--------------------+-----------------------------------+
| Shift+F7 | Shift+F7 | EXPLAIN ANALYZE query |
| Shift + F7 | Shift + F7 | EXPLAIN ANALYZE query |
+--------------------------+--------------------+-----------------------------------+
| F8 | F8 | Execute query to CSV file |
+--------------------------+--------------------+-----------------------------------+
@@ -190,7 +190,7 @@ When using the Debugger, the following shortcuts are available:
+--------------------------+--------------------+-----------------------------------+
| <accesskey> + s | <accesskey> + s | Stop |
+--------------------------+--------------------+-----------------------------------+
| Alt + Shift + q | Alt + Shift + q | Enter or Edit values in Grid |
| Alt + Shift + q | Option + Shift + q | Enter or Edit values in Grid |
+--------------------------+--------------------+-----------------------------------+
Inner Panel Navigation
@@ -203,13 +203,13 @@ for inner panel navigation:
:class: longtable
:widths: 2 2 3
+--------------------------+---------------------------+------------------------------+
| Shortcut (Windows/Linux) | Shortcut (Mac) | Function |
+==========================+===========================+==============================+
| Alt + Shift + Right Arrow| Alt + Shift + Right Arrow | Move to next inner panel |
+--------------------------+---------------------------+------------------------------+
| Alt + Shift + Left Arrow | Alt + Shift + Left Arrow | Move to previous inner panel |
+--------------------------+---------------------------+------------------------------+
+--------------------------+---------------------+------------------------------+
| Shortcut (Windows/Linux) | Shortcut (Mac) | Function |
+==========================+=====================+==============================+
| Alt + Shift + Right | Alt + Shift + Right | Move to next inner panel |
+--------------------------+---------------------+------------------------------+
| Alt + Shift + Left | Alt + Shift + Left | Move to previous inner panel |
+--------------------------+---------------------+------------------------------+
Access Key
**********
@@ -221,14 +221,14 @@ default access keys for supported browsers.
:class: longtable
:widths: 2 1 1 1
+-------------------+------------+------------+------------+
| | Windows | Linux | Mac |
+===================+============+============+============+
| Internet Explorer | Alt | Alt | |
+-------------------+------------+------------+------------+
| Chrome | Alt | Alt | Ctrl+Alt |
+-------------------+------------+------------+------------+
| Firefox | Alt+Shift | Alt+Shift | Ctrl+Alt |
+-------------------+------------+------------+------------+
| Safari | Alt | | Ctrl+Alt |
+-------------------+------------+------------+------------+
+-------------------+-------------+-------------+---------------+
| | Windows | Linux | Mac |
+===================+=============+=============+===============+
| Internet Explorer | Alt | Alt | |
+-------------------+-------------+-------------+---------------+
| Chrome | Alt | Alt | Ctrl + Option |
+-------------------+-------------+-------------+---------------+
| Firefox | Alt + Shift | Alt + Shift | Ctrl + Option |
+-------------------+-------------+-------------+---------------+
| Safari | Alt | | Ctrl + Option |
+-------------------+-------------+-------------+---------------+
+1
View File
@@ -16,6 +16,7 @@ Use the fields in the *Login* dialog to authenticate your connection:
field.
* Provide your password in the *Password* field.
* Click the *Login* button to securely log into pgAdmin.
* Please note that, if the pgAdmin server is restarted then you will be logged out. You need to re-login to continue.
Recovering a Lost Password
**************************
+38
View File
@@ -0,0 +1,38 @@
.. _master_password:
************************
`Master Password`:index:
************************
A master password is required to secure and later unlock the saved server passwords. This is applicable only for desktop mode users.
* You are prompted to enter the master password when you open the window for the first time after starting the application.
* Once you set the master password, all the existing saved passwords will be re-encrypted using the master password.
* The server passwords which are saved in the SQLite DB file are encrypted and decrypted using the master password.
.. image:: images/master_password_set.png
:alt: Set master password
:align: center
* You can disable the master password by setting the configuration parameter *MASTER_PASSWORD_REQUIRED=False*
* Note that, if master password is disabled, then all the saved passwords will be removed.
.. warning:: If master password is disabled, then the saved passwords will be encrypted using a key
which may not be as secure as master password. It is strongly recommended to use master password if you use "Save password" option.
* The master password is not stored anywhere on the physical storage. It is temporarily stored in the application memory and it does not get saved in case the application gets restarted.
* You are prompted to enter the master password when pgAdmin server is restarted.
.. image:: images/master_password_enter.png
:alt: Enter master password
:align: center
* If you forget the master password, you can use the "Reset Master Password" button to reset the password.
.. image:: images/master_password_reset.png
:alt: Reset master password
:align: center
.. warning:: Resetting the master password will also remove all the saved passwords and close all the existing established
connections.
+3
View File
@@ -11,6 +11,9 @@ notes for it.
.. toctree::
:maxdepth: 1
release_notes_4_7
release_notes_4_6
release_notes_4_5
release_notes_4_4
release_notes_4_3
release_notes_4_2
+17
View File
@@ -0,0 +1,17 @@
***********
Version 4.5
***********
Release date: 2019-04-10
This release contains a number of new features and fixes reported since the
release of pgAdmin4 4.4.
Bug fixes
*********
| `Bug #2214 <https://redmine.postgresql.org/issues/2214>`_ - Fixed 'Change Password' issue for SCRAM authentication.
| `Bug #3656 <https://redmine.postgresql.org/issues/3656>`_ - Ensure that two consecutive SELECT statements should work properly.
| `Bug #4131 <https://redmine.postgresql.org/issues/4131>`_ - Relabel the Save button on the datagrid text editor to avoid confusion with the actual Save button that updates the database.
| `Bug #4142 <https://redmine.postgresql.org/issues/4142>`_ - Added recommended ESLinter checks.
| `Bug #4143 <https://redmine.postgresql.org/issues/4143>`_ - Ensure that pgAdmin4 should work properly with psycopg2 v2.8
+33
View File
@@ -0,0 +1,33 @@
***********
Version 4.6
***********
Release date: 2019-05-02
This release contains a number of new features and fixes reported since the
release of pgAdmin4 4.5
Features
********
| `Feature #4165 <https://redmine.postgresql.org/issues/4165>`_ - Depend on psycopg2-binary in the Python wheel, rather than psycopg2.
Bug fixes
*********
| `Bug #2392 <https://redmine.postgresql.org/issues/2392>`_ - Ensure that on clicking Delete button should not delete rows immediately from the database server, it should be deleted when Save button will be clicked.
| `Bug #3327 <https://redmine.postgresql.org/issues/3327>`_ - Ensure that newly added row in backgrid should be visible.
| `Bug #3582 <https://redmine.postgresql.org/issues/3582>`_ - Ensure that JSON strings as comments should be added properly for all the objects.
| `Bug #3605 <https://redmine.postgresql.org/issues/3605>`_ - Fix an issue where Deleting N number of rows makes first N number of rows disable.
| `Bug #3938 <https://redmine.postgresql.org/issues/3938>`_ - Added support for Default Partition.
| `Bug #4087 <https://redmine.postgresql.org/issues/4087>`_ - Fix an issue where 'GRANT UPDATE' sql should be displayed for default sequence privileges.
| `Bug #4101 <https://redmine.postgresql.org/issues/4101>`_ - Ensure that confirmation dialog should be popped up before reload of query tool or debugger if it is opened in a new browser tab.
| `Bug #4104 <https://redmine.postgresql.org/issues/4104>`_ - Ensure that record should be add/edited for root partition table with primary keys.
| `Bug #4121 <https://redmine.postgresql.org/issues/4121>`_ - Fixed alignment issue of columns in definition section of Index node.
| `Bug #4134 <https://redmine.postgresql.org/issues/4134>`_ - Fixed 'Location cannot be empty' error when open Tablespace properties.
| `Bug #4138 <https://redmine.postgresql.org/issues/4138>`_ - Fix an issue where the dropdown becomes misaligned/displaced.
| `Bug #4154 <https://redmine.postgresql.org/issues/4154>`_ - Ensure the treeview shows all sequences except those used to implement IDENTITY columns (which can be edited as part of the column). Show all if Show System Objects is enabled.
| `Bug #4160 <https://redmine.postgresql.org/issues/4160>`_ - Fixed 'Increment value cannot be empty' error for existing tables.
| `Bug #4161 <https://redmine.postgresql.org/issues/4161>`_ - Ensure that parameters of procedures for EPAS server 10 and below should be set/reset properly.
| `Bug #4163 <https://redmine.postgresql.org/issues/4163>`_ - Prevent duplicate columns being included in reverse engineered SQL for tables.
| `Bug #4182 <https://redmine.postgresql.org/issues/4182>`_ - Ensure sanity of the permissions on the storage and session directories and the config database.
+34
View File
@@ -0,0 +1,34 @@
***********
Version 4.7
***********
Release date: 2019-05-30
This release contains a number of bug fixes since the release of pgAdmin4 4.6.
Bug fixes
*********
| `Bug #3377 <https://redmine.postgresql.org/issues/3377>`_ - In server mode, update all the saved server credentials when user password is changed.
| `Bug #3885 <https://redmine.postgresql.org/issues/3885>`_ - Fix the responsive layout of the main menu bar.
| `Bug #4162 <https://redmine.postgresql.org/issues/4162>`_ - Fix syntax error when adding more than one column to the existing table.
| `Bug #4164 <https://redmine.postgresql.org/issues/4164>`_ - Fix file browser path issue which occurs when client is on Windows and server is on Mac/Linux.
| `Bug #4184 <https://redmine.postgresql.org/issues/4184>`_ - Added Master Password to increase the security of saved passwords.
| `Bug #4194 <https://redmine.postgresql.org/issues/4194>`_ - Fix accessibility issue for menu navigation.
| `Bug #4208 <https://redmine.postgresql.org/issues/4208>`_ - Update the UI logo.
| `Bug #4217 <https://redmine.postgresql.org/issues/4217>`_ - Fixed CSRF security vulnerability issue, per Alvin Lindstam
| `Bug #4218 <https://redmine.postgresql.org/issues/4218>`_ - Properly assign dropdownParent in Select2 controls.
| `Bug #4219 <https://redmine.postgresql.org/issues/4219>`_ - Ensure popper.js is installed when needed.
| `Bug #4227 <https://redmine.postgresql.org/issues/4227>`_ - Fixed Tab key navigation for Maintenance dialog.
| `Bug #4244 <https://redmine.postgresql.org/issues/4244>`_ - Fix Tab key issue for Toggle switch controls and button on the dialog footer in Safari browser.
| `Bug #4245 <https://redmine.postgresql.org/issues/4245>`_ - Ensure that element should get highlighted when they get focus on using Tab key.
| `Bug #4246 <https://redmine.postgresql.org/issues/4246>`_ - Fixed console error when subnode control is used in panels.
| `Bug #4261 <https://redmine.postgresql.org/issues/4261>`_ - Stop using application/x-javascript as a mime type and use the RFC-compliant application/javascript instead.
| `Bug #4262 <https://redmine.postgresql.org/issues/4262>`_ - Fixed error on displaying table properties of a table partitioned by list having a default partition.
| `Bug #4263 <https://redmine.postgresql.org/issues/4263>`_ - Fix handling of JSON in the Query Tool with NULL elements.
| `Bug #4269 <https://redmine.postgresql.org/issues/4269>`_ - Fix navigation of switch cells in grids.
| `Bug #4275 <https://redmine.postgresql.org/issues/4275>`_ - Clarify wording for the NO INHERIT option on constraints, per Michel Feinstein.
| `Bug #4276 <https://redmine.postgresql.org/issues/4276>`_ - Relax the permission check on the directory containing the config database, as it may fail in some environments such as OpenShift.
| `Bug #4278 <https://redmine.postgresql.org/issues/4278>`_ - Prevent Backgrid Password cells from losing focus if the browser opens an autocomplete list.
| `Bug #4284 <https://redmine.postgresql.org/issues/4284>`_ - Fix syntax error when creating a table with a serial column.
+5 -3
View File
@@ -264,8 +264,9 @@ Use the fields in the *Definition* tab to define the check constraint:
* Provide the expression that a row must satisfy in the *Check* field. This
field is required.
* Move the *No Inherit?* switch to the *Yes* position to specify this constraint
is automatically inherited by a table's children. The default is *No*.
* Move the *No Inherit?* switch to the *Yes* position to specify that this
constraint is not automatically inherited by a table's children. The default
is *No*, meaning that the constraint will be inherited by any children.
* Move the *Don't validate?* switch to the *No* position to skip validation of
existing data; the constraint may not hold for all rows in the table. The
default is *Yes*.
@@ -442,6 +443,7 @@ icon (+) to add each partition:
* Move the *Operation* switch to *attach* to attach the partition, by default it
is *create*.
* Use the *Name* field to add the name of the partition.
* If partition type is Range or List then *Default* field will be enabled.
* If partition type is Range then *From* and *To* fields will be enabled.
* If partition type is List then *In* field will be enabled.
* If partition type is Hash then *Modulus* and *Remainder* fields will be
@@ -523,4 +525,4 @@ three columns and a primary key constraint on the *category_id* column.
* Click the *Info* button (i) to access online help.
* Click the *Save* button to save work.
* Click the *Cancel* button to exit without saving work.
* Click the *Reset* button to restore configuration parameters.
* Click the *Reset* button to restore configuration parameters.
+2 -2
View File
@@ -22,8 +22,8 @@ RUN yarn install --cache-folder ./ycache --verbose && \
FROM python:3.7-alpine3.9 as docs-builder
# Install only dependencies absolutely required for documentation building
RUN apk add --no-cache make && \
pip install --no-cache-dir sphinx flask_security flask_paranoid python-dateutil flask_sqlalchemy flask_gravatar flask_migrate simplejson
RUN apk add --no-cache make build-base openssl-dev libffi-dev && \
pip install --no-cache-dir sphinx flask_security flask_paranoid python-dateutil flask_sqlalchemy flask_gravatar flask_migrate simplejson cryptography
COPY ./pgadmin4/ /pgadmin4
+1 -1
View File
@@ -29,7 +29,7 @@ for src in "${DMG_SOURCES}"; do
done
echo "Creating image"
${HDIUTIL} create -quiet -srcfolder "$DMG_DIR" -format UDZO -volname "${DMG_VOLUME_NAME}" -ov "${DMG_IMAGE}" || exit 1
${HDIUTIL} create -quiet -srcfolder "$DMG_DIR" -fs HFS+ -format UDZO -volname "${DMG_VOLUME_NAME}" -ov "${DMG_IMAGE}" || exit 1
rm -rf "${DMG_DIR}" || exit 1
echo "Attaching License to image"
+5
View File
@@ -37,6 +37,11 @@ with open(req_file, 'r') as reqf:
for index, req in enumerate(required):
if ";" in req or req.startswith("#") or req == "":
required.remove(req)
continue
# Ensure the Wheel will use psycopg2-binary, not the source distro
if 'psycopg2' in req:
required[index] = req.replace('psycopg2', 'psycopg2-binary')
# Get the app version
if sys.version_info[:2] >= (3, 3):
+1 -1
View File
@@ -21,7 +21,7 @@ sqlparse==0.2.4
WTForms==2.2.1
Flask-Paranoid==0.2.0
psutil==5.5.1
psycopg2>=2.7.7
psycopg2>=2.8
python-dateutil>=2.8.0
htmlmin==0.1.12
Flask-HTMLmin==1.5.0
+2 -2
View File
@@ -8,9 +8,9 @@
<key>CFBundleName</key>
<string>pgAdmin 4</string>
<key>CFBundleShortVersionString</key>
<string>4.4.0</string>
<string>4.7.0</string>
<key>CFBundleVersion</key>
<string>4.4.0</string>
<string>4.7.0</string>
<key>LSMinimumSystemVersion</key>
<string>10.10</string>
<key>NSHumanReadableCopyright</key>
+1 -1
View File
@@ -1,4 +1,4 @@
VERSION = 4.4.0.0
VERSION = 4.7.0.0
QMAKE_TARGET_COMPANY = "The pgAdmin Development Team"
QMAKE_TARGET_PRODUCT = "pgAdmin 4"
QMAKE_TARGET_DESCRIPTION = "pgAdmin 4 Desktop Runtime"
+204
View File
@@ -0,0 +1,204 @@
# -*- coding: utf-8 -*-
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2019, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
# Get the correct version of chromedriver for the version of Chrome on the
# machine and save it to the specified location.
import argparse
import os
import platform
import subprocess
import sys
import urllib.request
import zipfile
def read_command_line():
"""Read the command line arguments.
Returns:
ArgumentParser: The parsed arguments object
"""
parser = argparse.ArgumentParser(
description='Get the correct version of chromedriver for the '
'specified Chrome installation and save it.')
if platform.system() != 'Windows':
parser.add_argument("chrome", metavar="CHROME",
help="the Chrome executable")
parser.add_argument("directory", metavar="DIRECTORY",
help="the directory in which to save chromedriver")
args = parser.parse_args()
return args
def get_chrome_version(args):
"""Get the Chrome version number.
Args:
args: The parsed arguments object
Returns:
The Chrome version number
"""
if platform.system() == 'Windows':
# On Windows we need to examine the resource section of the binary
import winreg
def _read_registry(root, key, value):
try:
hkey = winreg.OpenKey(root, key)
except Exception as e:
return None
try:
(val, typ) = winreg.QueryValueEx(hkey, value)
except Exception as e:
winreg.CloseKey(hkey)
return None
winreg.CloseKey(hkey)
return val
key = r'SOFTWARE\Google\Chrome\BLBeacon'
version_str = _read_registry(winreg.HKEY_CURRENT_USER, key, 'Version')
# On a 64b host, look for a 32b installation
if not version_str:
key = r'SOFTWARE\Wow6432Node\Google\Chrome\BLBeacon'
version_str = _read_registry(winreg.HKEY_CURRENT_USER, key,
'Version')
if not version_str:
print('The Chrome version could not be read from the registry.')
sys.exit(1)
chrome_version = '.'.join(version_str.split()[-1].split('.')[:-1])
else:
# On Linux/Mac we run the Chrome executable with the --version flag,
# then parse the output.
try:
result = subprocess.run([args.chrome, '--version'],
stdout=subprocess.PIPE)
except FileNotFoundError as e:
print('The specified Chrome executable could not be found.')
sys.exit(1)
version_str = result.stdout.decode("utf-8").strip()
# Check for 'Chrom' not 'Chrome' in case the user is using Chromium.
if "Chrom" not in version_str:
print('The specified Chrome executable output an unexpected '
'version string: {}.'.format(version_str))
sys.exit(1)
chrome_version = '.'.join(version_str.split()[-1].split('.')[:-1])
if chrome_version.count('.') != 2:
print('The specified Chrome executable output an unexpected '
'version string: {}.'.format(version_str))
sys.exit(1)
return chrome_version
def get_chromedriver_version(chrome_version):
"""Get the required chromedriver version number.
Args:
chrome_version: The chrome version number
Returns:
The chromedriver version number
"""
url = 'https://chromedriver.storage.googleapis.com/LATEST_RELEASE_{}' \
.format(chrome_version)
try:
fp = urllib.request.urlopen(url)
except urllib.error.URLError as e:
print('The chromedriver catalog URL could not be accessed: {}'
.format(e))
sys.exit(1)
version = fp.read().decode("utf-8").strip()
fp.close()
return version
def get_system():
"""Get the system name as known to chromedriver
Returns:
The system name
"""
if platform.system() == 'Darwin':
return 'mac64'
elif platform.system() == 'Linux':
return 'linux64'
elif platform.system() == 'Windows':
return 'win32'
else:
print("Unknown or unsupported operating system: {}"
.format(platform.system()))
sys.exit(1)
"""The core structure of the app."""
# Read the command line options
args = read_command_line()
chrome_version = get_chrome_version(args)
# Check the directory exists
if not os.path.isdir(args.directory):
print('The specified output directory could not be accessed.')
sys.exit(1)
chromedriver_version = get_chromedriver_version(chrome_version)
system = get_system()
url = 'https://chromedriver.storage.googleapis.com/{}/chromedriver_{}.zip' \
.format(chromedriver_version, system)
print('Downloading chromedriver v{} for Chrome v{} on {}...'
.format(chromedriver_version, chrome_version, system))
try:
file, headers = urllib.request.urlretrieve(url)
except urllib.error.URLError as e:
print('The chromedriver download URL could not be accessed: {}'
.format(e))
sys.exit(1)
# Unzip chromedriver
print('Extracting chromedriver...')
found = False
fp = open(file, 'rb')
z = zipfile.ZipFile(fp)
for name in z.namelist():
if (system == 'win32' and name == 'chromedriver.exe') or \
(system != 'win32' and name == 'chromedriver'):
z.extract(name, args.directory)
found = True
fp.close()
if not found:
print("chromedriver could not be found in the downloaded archive: {}"
.format(file))
sys.exit(1)
# Set the permissions
if system == 'mac64' or system == 'linux64':
os.chmod(os.path.join(args.directory, 'chromedriver'), 0o755)
print('Chromedriver downloaded to: {}'.format(args.directory))
+5
View File
@@ -14,6 +14,9 @@ module.exports = {
'amd': true,
'jasmine': true,
},
'extends': [
'eslint:recommended',
],
'parserOptions': {
'ecmaVersion': 2018,
'sourceType': 'module',
@@ -43,5 +46,7 @@ module.exports = {
'always-multiline'
],
'no-console': ["error", { allow: ["warn", "error"] }],
// We need to exclude below for RegEx case
"no-useless-escape": 0,
},
};
+12 -5
View File
@@ -50,7 +50,7 @@ APP_ICON = 'pg-icon'
# Application version number components
APP_RELEASE = 4
APP_REVISION = 4
APP_REVISION = 7
# Application version suffix, e.g. 'beta1', 'dev'. Usually an empty string
# for GA releases.
@@ -59,7 +59,7 @@ APP_SUFFIX = ''
# Numeric application version for upgrade checks. Should be in the format:
# [X]XYYZZ, where X is the release version, Y is the revision, with a leading
# zero if needed, and Z represents the suffix, with a leading zero if needed
APP_VERSION_INT = 40400
APP_VERSION_INT = 40700
# DO NOT CHANGE!
# The application version string, constructed from the components
@@ -123,6 +123,10 @@ if (not hasattr(builtins, 'SERVER_MODE')) or builtins.SERVER_MODE is None:
else:
SERVER_MODE = builtins.SERVER_MODE
# HTTP headers to search for CSRF token when it is not provided in the form.
# Default is ['X-CSRFToken', 'X-CSRF-Token']
WTF_CSRF_HEADERS = ['X-pgA-CSRFToken']
# User ID (email address) to use for the default user in desktop mode.
# The default should be fine here, as it's not exposed in the app.
DESKTOP_USER = 'pgadmin4@pgadmin.org'
@@ -141,9 +145,6 @@ DEFAULT_SERVER = '127.0.0.1'
# environment by the runtime
DEFAULT_SERVER_PORT = 5050
# Enable CSRF protection?
CSRF_ENABLED = True
# Enable X-Frame-Option protection.
# Set to one of "SAMEORIGIN", "ALLOW-FROM origin" or "" to disable.
# Note that "DENY" is NOT supported (and will be silently ignored).
@@ -418,6 +419,12 @@ SUPPORT_SSH_TUNNEL = True
# Set to False to disable password saving.
ALLOW_SAVE_TUNNEL_PASSWORD = False
##########################################################################
# Master password is used to encrypt/decrypt saved server passwords
# Applicable for desktop mode only
##########################################################################
MASTER_PASSWORD_REQUIRED = True
##########################################################################
# Local config settings
##########################################################################
+51
View File
@@ -0,0 +1,51 @@
"""empty message
Revision ID: 35f29b1701bd
Revises: ec1cac3399c9
Create Date: 2019-04-26 16:38:08.368471
"""
import base64
import os
import sys
from pgadmin.model import db, Server
# revision identifiers, used by Alembic.
revision = '35f29b1701bd'
down_revision = 'ec1cac3399c9'
branch_labels = None
depends_on = None
def upgrade():
db.engine.execute("ALTER TABLE user RENAME TO user_old")
db.engine.execute("""
CREATE TABLE user (
id INTEGER NOT NULL,
email VARCHAR(256) NOT NULL,
password VARCHAR(256),
active BOOLEAN NOT NULL,
confirmed_at DATETIME,
masterpass_check VARCHAR(256),
PRIMARY KEY (id),
UNIQUE (email),
CHECK (active IN (0, 1))
);
""")
db.engine.execute("""
INSERT INTO user (
id, email, password, active, confirmed_at
) SELECT
id, email, password, active, confirmed_at
FROM user_old""")
db.engine.execute("DROP TABLE user_old")
def downgrade():
pass
+4 -1
View File
@@ -31,7 +31,10 @@ def upgrade():
Security(app, user_datastore, register_blueprint=False)
else:
app.config['SECURITY_PASSWORD_SALT'] = current_salt
users = User.query.all()
users = User.query.with_entities(
User.id, User.email, User.password, User.active, User.confirmed_at)\
.all()
# This will upgrade the plaintext password of all the user as per the
# SECURITY_PASSWORD_HASH.
for user in users:
+1 -2
View File
@@ -67,7 +67,7 @@
"immutability-helper": "^3.0.0",
"imports-loader": "^0.8.0",
"ip-address": "^5.8.9",
"jquery": "3.3.1",
"jquery": "^3.4.0",
"jquery-contextmenu": "^2.8.0",
"jquery-ui": "^1.12.1",
"json-bignumber": "^1.0.1",
@@ -75,7 +75,6 @@
"moment": "^2.24.0",
"moment-timezone": "^0.5.23",
"mousetrap": "^1.6.3",
"prop-types": "^15.7.2",
"requirejs": "~2.3.6",
"select2": "^4.0.6-rc.1",
"shim-loader": "^1.0.1",
+36 -4
View File
@@ -23,20 +23,21 @@ from flask_login import user_logged_in, user_logged_out
from flask_mail import Mail
from flask_paranoid import Paranoid
from flask_security import Security, SQLAlchemyUserDatastore, current_user
from flask_security.utils import login_user
from flask_security.utils import login_user, logout_user
from werkzeug.datastructures import ImmutableDict
from werkzeug.local import LocalProxy
from werkzeug.utils import find_modules
from pgadmin.model import db, Role, Server, ServerGroup, \
User, Keys, Version, SCHEMA_VERSION as CURRENT_SCHEMA_VERSION
from pgadmin.utils import PgAdminModule, driver
from pgadmin.utils import PgAdminModule, driver, KeyManager
from pgadmin.utils.preferences import Preferences
from pgadmin.utils.session import create_session_interface, pga_unauthorised
from pgadmin.utils.versioned_template_loader import VersionedTemplateLoader
from datetime import timedelta
from pgadmin.setup import get_version, set_version
from pgadmin.utils.ajax import internal_server_error
from pgadmin.utils.csrf import pgCSRFProtect
# If script is running under python3, it will not have the xrange function
@@ -338,6 +339,9 @@ def create_app(app_name=None):
set_version(CURRENT_SCHEMA_VERSION)
db.session.commit()
if os.name != 'nt':
os.chmod(config.SQLITE_PATH, 0o600)
Mail(app)
import pgadmin.utils.paths as paths
@@ -364,7 +368,10 @@ def create_app(app_name=None):
'CSRF_SESSION_KEY': config.CSRF_SESSION_KEY,
'SECRET_KEY': config.SECRET_KEY,
'SECURITY_PASSWORD_SALT': config.SECURITY_PASSWORD_SALT,
'SESSION_COOKIE_DOMAIN': config.SESSION_COOKIE_DOMAIN
'SESSION_COOKIE_DOMAIN': config.SESSION_COOKIE_DOMAIN,
# CSRF Token expiration till session expires
'WTF_CSRF_TIME_LIMIT': getattr(config, 'CSRF_TIME_LIMIT', None),
'WTF_CSRF_METHODS': ['GET', 'POST', 'PUT', 'DELETE'],
}))
security.init_app(app, user_datastore)
@@ -567,12 +574,23 @@ def create_app(app_name=None):
def force_session_write(app, user):
session.force_write = True
@user_logged_in.connect_via(app)
def store_crypt_key(app, user):
# in desktop mode, master password is used to encrypt/decrypt
# and is stored in the keyManager memory
if config.SERVER_MODE:
if 'password' in request.form:
current_app.keyManager.set(request.form['password'])
@user_logged_out.connect_via(app)
def current_user_cleanup(app, user):
from config import PG_DEFAULT_DRIVER
from pgadmin.utils.driver import get_driver
from flask import current_app
# remove key
current_app.keyManager.reset()
for mdl in current_app.logout_hooks:
try:
mdl.on_logout(user)
@@ -623,6 +641,12 @@ def create_app(app_name=None):
abort(401)
login_user(user)
# if the server is restarted the in memory key will be lost
# but the user session may still be active. Logout the user
# to get the key again when login
if config.SERVER_MODE and current_app.keyManager.get() is None:
logout_user()
@app.after_request
def after_request(response):
if 'key' in request.args:
@@ -703,8 +727,16 @@ def create_app(app_name=None):
current_app.logger.error(e, exc_info=True)
return e
# Intialize the key manager
app.keyManager = KeyManager()
##########################################################################
# Protection against CSRF attacks
##########################################################################
with app.app_context():
pgCSRFProtect.init_app(app)
##########################################################################
# All done!
##########################################################################
return app
+158 -35
View File
@@ -18,7 +18,7 @@ from socket import error as SOCKETErrorException
import six
from flask import current_app, render_template, url_for, make_response, \
flash, Response, request, after_this_request, redirect
flash, Response, request, after_this_request, redirect, session
from flask_babelex import gettext
from flask_gravatar import Gravatar
from flask_login import current_user, login_required
@@ -37,9 +37,13 @@ from pgadmin import current_blueprint
from pgadmin.settings import get_setting
from pgadmin.utils import PgAdminModule
from pgadmin.utils.ajax import make_json_response
from pgadmin.utils.csrf import pgCSRFProtect
from pgadmin.utils.preferences import Preferences
from pgadmin.browser.register_browser_preferences import \
register_browser_preferences
from pgadmin.utils.master_password import validate_master_password, \
set_masterpass_check_text, cleanup_master_password, get_crypt_key, \
set_crypt_key, process_masterpass_disabled
try:
import urllib.request as urlreq
@@ -219,7 +223,10 @@ class BrowserModule(PgAdminModule):
Returns:
list: a list of url endpoints exposed to the client.
"""
return ['browser.index', 'browser.nodes']
return ['browser.index', 'browser.nodes',
'browser.check_master_password',
'browser.set_master_password',
'browser.reset_master_password']
blueprint = BrowserModule(MODULE_NAME, __name__)
@@ -478,6 +485,7 @@ class BrowserPluginModule(PgAdminModule):
@blueprint.route("/")
@pgCSRFProtect.exempt
@login_required
def index():
"""Render and process the main browser window."""
@@ -561,6 +569,7 @@ def index():
@blueprint.route("/js/utils.js")
@pgCSRFProtect.exempt
@login_required
def utils():
layout = get_setting('Browser/Layout', default='')
@@ -623,61 +632,37 @@ def utils():
pg_libpq_version=pg_libpq_version,
support_ssh_tunnel=config.SUPPORT_SSH_TUNNEL
),
200, {'Content-Type': 'application/x-javascript'})
200, {'Content-Type': 'application/javascript'})
@blueprint.route("/js/endpoints.js")
@pgCSRFProtect.exempt
def exposed_urls():
return make_response(
render_template('browser/js/endpoints.js'),
200, {'Content-Type': 'application/x-javascript'}
200, {'Content-Type': 'application/javascript'}
)
@blueprint.route("/js/error.js")
@pgCSRFProtect.exempt
@login_required
def error_js():
return make_response(
render_template('browser/js/error.js', _=gettext),
200, {'Content-Type': 'application/x-javascript'})
@blueprint.route("/js/node.js")
@login_required
def node_js():
prefs = Preferences.module('paths')
pg_help_path_pref = prefs.preference('pg_help_path')
pg_help_path = pg_help_path_pref.get()
edbas_help_path_pref = prefs.preference('edbas_help_path')
edbas_help_path = edbas_help_path_pref.get()
return make_response(
render_template('browser/js/node.js',
pg_help_path=pg_help_path,
edbas_help_path=edbas_help_path,
_=gettext
),
200, {'Content-Type': 'application/x-javascript'})
200, {'Content-Type': 'application/javascript'})
@blueprint.route("/js/messages.js")
@pgCSRFProtect.exempt
def messages_js():
return make_response(
render_template('browser/js/messages.js', _=gettext),
200, {'Content-Type': 'application/x-javascript'})
@blueprint.route("/js/collection.js")
@login_required
def collection_js():
return make_response(
render_template('browser/js/collection.js', _=gettext),
200, {'Content-Type': 'application/x-javascript'})
200, {'Content-Type': 'application/javascript'})
@blueprint.route("/browser.css")
@pgCSRFProtect.exempt
@login_required
def browser_css():
"""Render and return CSS snippets from the nodes and modules."""
@@ -703,6 +688,133 @@ def get_nodes():
return make_json_response(data=nodes)
def form_master_password_response(existing=True, present=False, errmsg=None):
content_new = (
gettext("Set Master Password"),
"<br/>".join([
gettext("Please set a master password for pgAdmin."),
gettext("This will be used to secure and later unlock saved "
"passwords and other credentials.")])
)
content_existing = (
gettext("Unlock Saved Passwords"),
"<br/>".join([
gettext("Please enter your master password."),
gettext("This is required to unlock saved passwords and "
"reconnect to the database server(s).")])
)
return make_json_response(data={
'present': present,
'title': content_existing[0] if existing else content_new[0],
'content': render_template(
'browser/master_password.html',
content_text=content_existing[1] if existing else content_new[1],
errmsg=errmsg
),
'reset': existing
})
@blueprint.route("/master_password", endpoint="check_master_password",
methods=["GET"])
def check_master_password():
"""
Checks if the master password is available in the memory
This password will be used to encrypt/decrypt saved server passwords
"""
return make_json_response(data=get_crypt_key()[0])
@blueprint.route("/master_password", endpoint="reset_master_password",
methods=["DELETE"])
def reset_master_password():
"""
Removes the master password and remove all saved passwords
This password will be used to encrypt/decrypt saved server passwords
"""
cleanup_master_password()
return make_json_response(data=get_crypt_key()[0])
@blueprint.route("/master_password", endpoint="set_master_password",
methods=["POST"])
def set_master_password():
"""
Set the master password and store in the memory
This password will be used to encrypt/decrypt saved server passwords
"""
data = None
if hasattr(request.data, 'decode'):
data = request.data.decode('utf-8')
if data != '':
data = json.loads(data)
# Master password is not applicable for server mode
if not config.SERVER_MODE and config.MASTER_PASSWORD_REQUIRED:
if data != '' and data.get('password', '') != '':
# if master pass is set previously
if current_user.masterpass_check is not None:
if not validate_master_password(data.get('password')):
return form_master_password_response(
existing=True,
present=False,
errmsg=gettext("Incorrect master password")
)
# store the master pass in the memory
set_crypt_key(data.get('password'))
if current_user.masterpass_check is None:
# master check is not set, which means the server password
# data is old and is encrypted with old key
# Re-encrypt with new key
from pgadmin.browser.server_groups.servers.utils \
import reencrpyt_server_passwords
reencrpyt_server_passwords(
current_user.id, current_user.password,
data.get('password'))
# set the encrypted sample text with the new
# master pass
set_masterpass_check_text(data.get('password'))
elif not get_crypt_key()[0] and \
current_user.masterpass_check is not None:
return form_master_password_response(
existing=True,
present=False,
)
elif not get_crypt_key()[0]:
return form_master_password_response(
existing=False,
present=False,
)
# if master password is disabled now, but was used once then
# remove all the saved passwords
process_masterpass_disabled()
if config.SERVER_MODE and current_user.masterpass_check is None:
crypt_key = get_crypt_key()[1]
from pgadmin.browser.server_groups.servers.utils \
import reencrpyt_server_passwords
reencrpyt_server_passwords(
current_user.id, current_user.password, crypt_key)
set_masterpass_check_text(crypt_key)
return form_master_password_response(
present=True,
)
# Only register route if SECURITY_CHANGEABLE is set to True
# We can't access app context here so cannot
# use app.config['SECURITY_CHANGEABLE']
@@ -711,6 +823,7 @@ def get_nodes():
if hasattr(config, 'SECURITY_CHANGEABLE') and config.SECURITY_CHANGEABLE:
@blueprint.route("/change_password", endpoint="change_password",
methods=['GET', 'POST'])
@pgCSRFProtect.exempt
@login_required
def change_password():
"""View function which handles a change password request."""
@@ -760,6 +873,15 @@ if hasattr(config, 'SECURITY_CHANGEABLE') and config.SECURITY_CHANGEABLE:
if request.json is None and not has_error:
after_this_request(_commit)
do_flash(*get_message('PASSWORD_CHANGE'))
old_key = get_crypt_key()[1]
set_crypt_key(form.new_password.data, False)
from pgadmin.browser.server_groups.servers.utils \
import reencrpyt_server_passwords
reencrpyt_server_passwords(
current_user.id, old_key, form.new_password.data)
return redirect(get_url(_security.post_change_view) or
get_url(_security.post_login_view))
@@ -794,6 +916,7 @@ if hasattr(config, 'SECURITY_RECOVERABLE') and config.SECURITY_RECOVERABLE:
@blueprint.route("/reset_password", endpoint="forgot_password",
methods=['GET', 'POST'])
@pgCSRFProtect.exempt
@anonymous_user_required
def forgot_password():
"""View function that handles a forgotten password request."""
@@ -860,10 +983,10 @@ if hasattr(config, 'SECURITY_RECOVERABLE') and config.SECURITY_RECOVERABLE:
methods=['GET', 'POST'],
endpoint='reset_password'
)
@pgCSRFProtect.exempt
@anonymous_user_required
def reset_password(token):
"""View function that handles a reset password request."""
expired, invalid, user = reset_password_token_status(token)
if invalid:
@@ -26,6 +26,8 @@ import config
from config import PG_DEFAULT_DRIVER
from pgadmin.model import db, Server, ServerGroup, User
from pgadmin.utils.driver import get_driver
from pgadmin.utils.master_password import get_crypt_key
from pgadmin.utils.exception import CryptKeyMissing
def has_any(data, keys):
@@ -117,9 +119,16 @@ class ServerModule(sg.ServerGroupPluginModule):
driver = get_driver(PG_DEFAULT_DRIVER)
for server in servers:
manager = driver.connection_manager(server.id)
conn = manager.connection()
connected = conn.connected()
connected = False
manager = None
try:
manager = driver.connection_manager(server.id)
conn = manager.connection()
connected = conn.connected()
except CryptKeyMissing:
# show the nodes at least even if not able to connect.
pass
in_recovery = None
wal_paused = None
@@ -723,6 +732,11 @@ class ServerNode(PGChildNodeView):
request.data, encoding='utf-8'
)
# Get enc key
crypt_key_present, crypt_key = get_crypt_key()
if not crypt_key_present:
raise CryptKeyMissing
# Some fields can be provided with service file so they are optional
if 'service' in data and not data['service']:
required_args.extend([
@@ -807,7 +821,7 @@ class ServerNode(PGChildNodeView):
# login with password
have_password = True
password = data['password']
password = encrypt(password, current_user.password)
password = encrypt(password, crypt_key)
elif 'passfile' in data and data["passfile"] != '':
passfile = data['passfile']
setattr(server, 'passfile', passfile)
@@ -817,7 +831,7 @@ class ServerNode(PGChildNodeView):
have_tunnel_password = True
tunnel_password = data['tunnel_password']
tunnel_password = \
encrypt(tunnel_password, current_user.password)
encrypt(tunnel_password, crypt_key)
status, errmsg = conn.connect(
password=password,
@@ -930,7 +944,7 @@ class ServerNode(PGChildNodeView):
"servers/supported_servers.js",
server_types=ServerType.types()
),
200, {'Content-Type': 'application/x-javascript'}
200, {'Content-Type': 'application/javascript'}
)
def connect_status(self, gid, sid):
@@ -998,6 +1012,11 @@ class ServerNode(PGChildNodeView):
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
conn = manager.connection()
# Get enc key
crypt_key_present, crypt_key = get_crypt_key()
if not crypt_key_present:
raise CryptKeyMissing
# If server using SSH Tunnel
if server.use_ssh_tunnel:
if 'tunnel_password' not in data:
@@ -1014,12 +1033,12 @@ class ServerNode(PGChildNodeView):
# Encrypt the password before saving with user's login
# password key.
try:
tunnel_password = encrypt(tunnel_password, user.password) \
tunnel_password = encrypt(tunnel_password, crypt_key) \
if tunnel_password is not None else \
server.tunnel_password
except Exception as e:
current_app.logger.exception(e)
return internal_server_error(errormsg=e.message)
return internal_server_error(errormsg=str(e))
if 'password' not in data:
conn_passwd = getattr(conn, 'password', None)
@@ -1038,11 +1057,11 @@ class ServerNode(PGChildNodeView):
# Encrypt the password before saving with user's login
# password key.
try:
password = encrypt(password, user.password) \
password = encrypt(password, crypt_key) \
if password is not None else server.password
except Exception as e:
current_app.logger.exception(e)
return internal_server_error(errormsg=e.message)
return internal_server_error(errormsg=str(e))
# Check do we need to prompt for the database server or ssh tunnel
# password or both. Return the password template in case password is
@@ -1235,6 +1254,7 @@ class ServerNode(PGChildNodeView):
"""
try:
data = json.loads(request.form['data'], encoding='utf-8')
crypt_key = get_crypt_key()[1]
# Fetch Server Details
server = Server.query.filter_by(id=sid).first()
@@ -1292,7 +1312,7 @@ class ServerNode(PGChildNodeView):
# Check against old password only if no pgpass file
if not is_passfile:
decrypted_password = decrypt(manager.password, user.password)
decrypted_password = decrypt(manager.password, crypt_key)
if isinstance(decrypted_password, bytes):
decrypted_password = decrypted_password.decode()
@@ -1328,7 +1348,7 @@ class ServerNode(PGChildNodeView):
# Store password in sqlite only if no pgpass file
if not is_passfile:
password = encrypt(data['newPassword'], user.password)
password = encrypt(data['newPassword'], crypt_key)
# Check if old password was stored in pgadmin4 sqlite database.
# If yes then update that password.
if server.password is not None and config.ALLOW_SAVE_PASSWORD:
@@ -1472,6 +1492,7 @@ class ServerNode(PGChildNodeView):
def get_response_for_password(self, server, status, prompt_password=False,
prompt_tunnel_password=False, errmsg=None):
if server.use_ssh_tunnel:
return make_json_response(
success=0,
@@ -1498,7 +1519,7 @@ class ServerNode(PGChildNodeView):
server_label=server.name,
username=server.username,
errmsg=errmsg,
_=gettext
_=gettext,
)
)
@@ -166,7 +166,6 @@ class DatabaseView(PGChildNodeView):
def wrap(f):
@wraps(f)
def wrapped(self, *args, **kwargs):
self.manager = get_driver(
PG_DEFAULT_DRIVER
).connection_manager(
@@ -805,7 +804,12 @@ class DatabaseView(PGChildNodeView):
data = {}
for k, v in request.args.items():
try:
data[k] = json.loads(v, encoding='utf-8')
# comments should be taken as is because if user enters a
# json comment it is parsed by loads which should not happen
if k in ('comments',):
data[k] = v
else:
data[k] = json.loads(v, encoding='utf-8')
except ValueError:
data[k] = v
status, res = self.get_sql(gid, sid, data, did)
@@ -208,7 +208,7 @@ class CastView(PGChildNodeView):
).connection_manager(kwargs['sid'])
self.conn = self.manager.connection(did=kwargs['did'])
# Set template path for the SQL scripts
self.template_path = 'cast/sql/#{0}#'.format(self.manager.version)
self.template_path = 'casts/sql/#{0}#'.format(self.manager.version)
return f(*args, **kwargs)
@@ -555,7 +555,12 @@ class ForeignDataWrapperView(PGChildNodeView):
data = {}
for k, v in request.args.items():
try:
data[k] = json.loads(v, encoding='utf-8')
# comments should be taken as is because if user enters a
# json comment it is parsed by loads which should not happen
if k in ('description',):
data[k] = v
else:
data[k] = json.loads(v, encoding='utf-8')
except ValueError:
data[k] = v
try:
@@ -116,10 +116,6 @@ class ForeignServerView(PGChildNodeView):
* __init__(**kwargs)
- Method is used to initialize the ForeignServerView and it's base view.
* module_js()
- This property defines (if javascript) exists for this node.
Override this property for your own logic
* check_precondition()
- This function will behave as a decorator which will checks
database connection before running view, it will also attaches
@@ -192,23 +188,9 @@ class ForeignServerView(PGChildNodeView):
'msql': [{'get': 'msql'}, {'get': 'msql'}],
'stats': [{'get': 'statistics'}],
'dependency': [{'get': 'dependencies'}],
'dependent': [{'get': 'dependents'}],
'module.js': [{}, {}, {'get': 'module_js'}]
'dependent': [{'get': 'dependents'}]
})
def module_js(self):
"""
This property defines (if javascript) exists for this node.
Override this property for your own logic.
"""
return make_response(
render_template(
"foreign_servers/js/foreign_servers.js",
_=gettext
),
200, {'Content-Type': 'application/x-javascript'}
)
def check_precondition(f):
"""
This function will behave as a decorator which will checks
@@ -578,7 +560,12 @@ class ForeignServerView(PGChildNodeView):
data = {}
for k, v in request.args.items():
try:
data[k] = json.loads(v, encoding='utf-8')
# comments should be taken as is because if user enters a
# json comment it is parsed by loads which should not happen
if k in ('description',):
data[k] = v
else:
data[k] = json.loads(v, encoding='utf-8')
except ValueError:
data[k] = v
try:
@@ -132,10 +132,6 @@ class UserMappingView(PGChildNodeView):
* __init__(**kwargs)
- Method is used to initialize the UserMappingView and it's base view.
* module_js()
- This property defines (if javascript) exists for this node.
Override this property for your own logic
* check_precondition()
- This function will behave as a decorator which will checks
database connection before running view, it will also attaches
@@ -209,23 +205,9 @@ class UserMappingView(PGChildNodeView):
'msql': [{'get': 'msql'}, {'get': 'msql'}],
'stats': [{'get': 'statistics'}],
'dependency': [{'get': 'dependencies'}],
'dependent': [{'get': 'dependents'}],
'module.js': [{}, {}, {'get': 'module_js'}]
'dependent': [{'get': 'dependents'}]
})
def module_js(self):
"""
This property defines (if javascript) exists for this node.
Override this property for your own logic.
"""
return make_response(
render_template(
"user_mappings/js/user_mappings.js",
_=gettext
),
200, {'Content-Type': 'application/x-javascript'}
)
def check_precondition(f):
"""
This function will behave as a decorator which will checks
@@ -558,7 +558,12 @@ class LanguageView(PGChildNodeView):
data = {}
for k, v in request.args.items():
try:
data[k] = json.loads(v, encoding='utf-8')
# comments should be taken as is because if user enters a
# json comment it is parsed by loads which should not happen
if k in ('description',):
data[k] = v
else:
data[k] = json.loads(v, encoding='utf-8')
except ValueError:
data[k] = v
try:
@@ -237,7 +237,7 @@ class SchemaView(PGChildNodeView):
self.manager = None
self.conn = None
self.template_path = None
self.template_initial = 'schema'
self.template_initial = 'schemas'
@staticmethod
def ppas_template_path(ver):
@@ -724,7 +724,12 @@ It may have been removed by another user.
data = dict()
for k, v in request.args.items():
try:
data[k] = json.loads(v, encoding='utf-8')
# comments should be taken as is because if user enters a
# json comment it is parsed by loads which should not happen
if k in ('description',):
data[k] = v
else:
data[k] = json.loads(v, encoding='utf-8')
except ValueError:
data[k] = v
@@ -128,8 +128,7 @@ class CatalogObjectView(PGChildNodeView):
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
'sql': [{'get': 'sql'}],
'dependency': [{'get': 'dependencies'}],
'dependent': [{'get': 'dependents'}],
'module.js': [{}, {}, {'get': 'module_js'}]
'dependent': [{'get': 'dependents'}]
})
def check_precondition(f):
@@ -155,8 +155,7 @@ class CatalogObjectColumnsView(PGChildNodeView):
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
'sql': [{'get': 'sql'}],
'dependency': [{'get': 'dependencies'}],
'dependent': [{'get': 'dependents'}],
'module.js': [{}, {}, {'get': 'module_js'}]
'dependent': [{'get': 'dependents'}]
})
def check_precondition(f):
@@ -171,7 +171,6 @@ class CollationView(PGChildNodeView):
'stats': [{'get': 'statistics'}],
'dependency': [{'get': 'dependencies'}],
'dependent': [{'get': 'dependents'}],
'module.js': [{}, {}, {'get': 'module_js'}],
'get_collations': [{'get': 'get_collation'},
{'get': 'get_collation'}]
})
@@ -193,7 +192,7 @@ class CollationView(PGChildNodeView):
self.conn = self.manager.connection(did=kwargs['did'])
# Set the template path for the SQL scripts
self.template_path = compile_template_path(
'collation/sql/',
'collations/sql/',
self.manager.server_type,
self.manager.version
)
@@ -602,7 +601,12 @@ class CollationView(PGChildNodeView):
data = dict()
for k, v in request.args.items():
try:
data[k] = json.loads(v, encoding='utf-8')
# comments should be taken as is because if user enters a
# json comment it is parsed by loads which should not happen
if k in ('description',):
data[k] = v
else:
data[k] = json.loads(v, encoding='utf-8')
except ValueError:
data[k] = v
@@ -95,9 +95,6 @@ class DomainView(PGChildNodeView, DataTypeReader):
- Works as a decorator.
Validating request on the request of create, update and modified SQL.
* module_js():
- Load JS file (domains.js) for this module.
* check_precondition(f):
- Works as a decorator.
- Checks database connection status.
@@ -168,7 +165,6 @@ class DomainView(PGChildNodeView, DataTypeReader):
'stats': [{'get': 'statistics'}],
'dependency': [{'get': 'dependencies'}],
'dependent': [{'get': 'dependents'}],
'module.js': [{}, {}, {'get': 'module_js'}],
'get_types': [{'get': 'types'}, {'get': 'types'}],
'get_collations': [
{'get': 'get_collations'},
@@ -246,18 +242,6 @@ class DomainView(PGChildNodeView, DataTypeReader):
return wrap
def module_js(self):
"""
Load JS file (domains.js) for this module.
"""
return make_response(
render_template(
"domains/js/domains.js",
_=gettext
),
200, {'Content-Type': 'application/x-javascript'}
)
def check_precondition(f):
"""
Works as a decorator.
@@ -109,9 +109,6 @@ class ForeignTableView(PGChildNodeView, DataTypeReader):
- Works as a decorator.
Validating request on the request of create, update and modified SQL.
* module_js():
- Overrides this property to define javascript for Foreign Table node.
* check_precondition(f):
- Works as a decorator.
- Checks database connection status.
@@ -204,7 +201,6 @@ class ForeignTableView(PGChildNodeView, DataTypeReader):
'stats': [{'get': 'statistics'}],
'dependency': [{'get': 'dependencies'}],
'dependent': [{'get': 'dependents'}],
'module.js': [{}, {}, {'get': 'module_js'}],
'get_collations': [
{'get': 'get_collations'},
{'get': 'get_collations'}
@@ -319,18 +315,6 @@ class ForeignTableView(PGChildNodeView, DataTypeReader):
return wrap
def module_js(self):
"""
Load JS file (foreign_tables.js) for this module.
"""
return make_response(
render_template(
"foreign_tables/js/foreign_tables.js",
_=gettext
),
200, {'Content-Type': 'application/x-javascript'}
)
def check_precondition(f):
"""
Works as a decorator.
@@ -108,10 +108,6 @@ class FtsConfigurationView(PGChildNodeView):
- Method is used to initialize the FtsConfigurationView and it's base
view.
* module_js()
- This property defines (if javascript) exists for this node.
Override this property for your own logic
* check_precondition()
- This function will behave as a decorator which will checks
database connection before running view, it will also attaches
@@ -200,7 +196,6 @@ class FtsConfigurationView(PGChildNodeView):
'stats': [{'get': 'statistics'}],
'dependency': [{'get': 'dependencies'}],
'dependent': [{'get': 'dependents'}],
'module.js': [{}, {}, {'get': 'module_js'}],
'parsers': [{'get': 'parsers'},
{'get': 'parsers'}],
'copyConfig': [{'get': 'copyConfig'},
@@ -215,18 +210,6 @@ class FtsConfigurationView(PGChildNodeView):
self.manager = None
super(FtsConfigurationView, self).__init__(**kwargs)
def module_js(self):
"""
Load JS file (fts_configuration.js) for this module.
"""
return make_response(
render_template(
"fts_configuration/js/fts_configuration.js",
_=_
),
200, {'Content-Type': 'application/x-javascript'}
)
def check_precondition(f):
"""
This function will behave as a decorator which will checks
@@ -242,7 +225,7 @@ class FtsConfigurationView(PGChildNodeView):
kwargs['sid'])
self.conn = self.manager.connection(did=kwargs['did'])
# Set the template path for the SQL scripts
self.template_path = 'fts_configuration/sql/#{0}#'.format(
self.template_path = 'fts_configurations/sql/#{0}#'.format(
self.manager.version)
return f(*args, **kwargs)
@@ -612,7 +595,12 @@ class FtsConfigurationView(PGChildNodeView):
data = {}
for k, v in request.args.items():
try:
data[k] = json.loads(v, encoding='utf-8')
# comments should be taken as is because if user enters a
# json comment it is parsed by loads which should not happen
if k in ('description',):
data[k] = v
else:
data[k] = json.loads(v, encoding='utf-8')
except ValueError:
data[k] = v
@@ -105,10 +105,6 @@ class FtsDictionaryView(PGChildNodeView):
* __init__(**kwargs)
- Method is used to initialize the FtsDictionaryView and it's base view.
* module_js()
- This property defines (if javascript) exists for this node.
Override this property for your own logic
* check_precondition()
- This function will behave as a decorator which will checks
database connection before running view, it will also attaches
@@ -192,7 +188,6 @@ class FtsDictionaryView(PGChildNodeView):
'stats': [{'get': 'statistics'}],
'dependency': [{'get': 'dependencies'}],
'dependent': [{'get': 'dependents'}],
'module.js': [{}, {}, {'get': 'module_js'}],
'fetch_templates': [{'get': 'fetch_templates'},
{'get': 'fetch_templates'}],
})
@@ -203,18 +198,6 @@ class FtsDictionaryView(PGChildNodeView):
self.manager = None
super(FtsDictionaryView, self).__init__(**kwargs)
def module_js(self):
"""
Load JS file (fts_dictionary.js) for this module.
"""
return make_response(
render_template(
"fts_dictionary/js/fts_dictionary.js",
_=_
),
200, {'Content-Type': 'application/x-javascript'}
)
def check_precondition(f):
"""
This function will behave as a decorator which will checks
@@ -232,7 +215,7 @@ class FtsDictionaryView(PGChildNodeView):
driver = get_driver(PG_DEFAULT_DRIVER)
self.qtIdent = driver.qtIdent
# Set the template path for the SQL scripts
self.template_path = 'fts_dictionary/sql/#{0}#'.format(
self.template_path = 'fts_dictionaries/sql/#{0}#'.format(
self.manager.version)
return f(*args, **kwargs)
@@ -607,7 +590,12 @@ class FtsDictionaryView(PGChildNodeView):
data = {}
for k, v in request.args.items():
try:
data[k] = json.loads(v, encoding='utf-8')
# comments should be taken as is because if user enters a
# json comment it is parsed by loads which should not happen
if k in ('description',):
data[k] = v
else:
data[k] = json.loads(v, encoding='utf-8')
except ValueError:
data[k] = v
Loaded 100 of 909 files, more files were not shown because too many files have changed in this diff. Show more