Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/en_US/images/add_role.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/en_US/images/permissions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/en_US/images/roles.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/en_US/images/user.png
Binary file not shown.
Binary file added docs/en_US/images/users.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
88 changes: 73 additions & 15 deletions docs/en_US/user_management.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ When you authenticate with pgAdmin, the server definitions associated with that
login role are made available in the tree control.

Users Tab
*******************
*********
An administrative user can use the *Users* tab to:

* manage pgAdmin users
Expand All @@ -21,7 +21,7 @@ An administrative user can use the *Users* tab to:
* deactivate user
* unlock a locked user

.. image:: images/user.png
.. image:: images/users.png
:alt: pgAdmin user management window
:align: center

Expand Down Expand Up @@ -78,6 +78,60 @@ users, but otherwise have the same capabilities as those with the *User* role.
* Click the *Help* button (?) to access online help.


Roles Tab
*********
An administrative user can use the *Roles* tab to:

* manage pgAdmin roles
* delete roles

.. image:: images/roles.png
:alt: pgAdmin roles management window
:align: center

Use the *Search* field to specify criteria and review a list of roles
that match the specified criteria. You can enter a value that matches
the following criteria types: *Role Name* or *Description*.

To add a role, click the Add (+) button at the top left corner. It will open a
dialog where you can fill in details for the new role.

.. image:: images/add_role.png
:alt: pgAdmin roles management window add new role
:align: center

Provide information about the new pgAdmin role in the row:

* Use the *Name* field to specify a unique name for the role.
* Use the *Description* field to provide a brief description of the role.

To delete a role, click the trash icon to the left of the row and confirm deletion
in the *Delete role?* dialog. If the role is associated with any users or resources,
you may need to reassign those associations before deletion.

Roles allow administrators to group privileges and assign them to users more efficiently.
This helps in managing permissions and access control within the pgAdmin client.

* Click the *Refresh* button to get the latest roles list.
* Click the *Help* button (?) to access online help.


Permissions Tab
***************
An administrative user can use the *Permissions* tab to manage pgAdmin permissions for
a role.

.. image:: images/permissions.png
:alt: pgAdmin permissions management window
:align: center

* Filter permissions using the *Search* field by entering names that match the list.
* Administrators can select permissions from the list of available permissions, and
choose to grant or revoke these permissions for specific roles.
* The permissions are applied to the selected role immediately.



Using 'setup.py' command line script
####################################

Expand Down Expand Up @@ -108,10 +162,11 @@ email and password. role and active will be optional fields.

/path/to/python /path/to/setup.py add-user user1@gmail.com password

# to specify a role, admin and non-admin users:
# to specify a role, either you can use --admin for Administrator role or provide the
# role using --role. If both are provided --admin will be used:

/path/to/python /path/to/setup.py add-user user1@gmail.com password --admin
/path/to/python /path/to/setup.py add-user user1@gmail.com password --nonadmin
/path/to/python /path/to/setup.py add-user user1@gmail.com password --role Users

# to specify user's status

Expand All @@ -132,10 +187,11 @@ followed by email, password and authentication source. email, role and status wi

/path/to/python /path/to/setup.py add-external-user ldapuser ldap --email user1@gmail.com

# to specify a role, admin and non-admin user:
# to specify a role, either you can use --admin for Administrator role or provide the
# role using --role. If both are provided --admin will be used:

/path/to/python /path/to/setup.py add-external-user ldapuser ldap --admin
/path/to/python /path/to/setup.py add-external-user ldapuser ldap --nonadmin
/path/to/python /path/to/setup.py add-external-user ldapuser ldap --role Users

# to specify user's status

Expand All @@ -152,10 +208,11 @@ email address. password, role and active are updatable fields.

/path/to/python /path/to/setup.py update-user user1@gmail.com --password new-password

# to specify a role, admin and non-admin user:
# to specify a role, either you can use --admin for Administrator role or provide the
# role using --role. If both are provided --admin will be used:

/path/to/python /path/to/setup.py update-user user1@gmail.com password --role --admin
/path/to/python /path/to/setup.py update-user user1@gmail.com password --role --nonadmin
/path/to/python /path/to/setup.py update-user user1@gmail.com password --admin
/path/to/python /path/to/setup.py update-user user1@gmail.com password --role Users

# to specify user's status

Expand All @@ -172,17 +229,18 @@ followed by username and auth source. email, password, role and active are updat

# to change email address:

/path/to/python /path/to/setup.py update-external-user ldap ldapuser --email newemail@gmail.com
/path/to/python /path/to/setup.py update-external-user ldapuser --auth-source ldap --email newemail@gmail.com

# to specify a role, admin and non-admin user:
# to specify a role, either you can use --admin for Administrator role or provide the
# role using --role. If both are provided --admin will be used:

/path/to/python /path/to/setup.py update-user user1@gmail.com password --role --admin
/path/to/python /path/to/setup.py update-user user1@gmail.com password --role --nonadmin
/path/to/python /path/to/setup.py update-external-user user1@gmail.com password --role --admin
/path/to/python /path/to/setup.py update-external-user user1@gmail.com password --role --role Users

# to change user's status

/path/to/python /path/to/setup.py update-user ldap ldapuser --active
/path/to/python /path/to/setup.py update-user ldap ldapuser --inactive
/path/to/python /path/to/setup.py update-user ldapuser --auth-source ldap --active
/path/to/python /path/to/setup.py update-user ldapuser --auth-source ldap --inactive

Delete User
***********
Expand Down
14 changes: 14 additions & 0 deletions web/migrations/versions/1f0eddc8fc79_.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@ def upgrade():
sa.Column('db_res_type', sa.String(length=32),
server_default=RESTRICTION_TYPE_DATABASES))

# For adding custom role permissions
op.add_column('role', sa.Column('permissions', sa.Text()))

# get metadata from current connection
meta = sa.MetaData()
# define table representation
meta.reflect(op.get_bind(), only=('role',))
role_table = sa.Table('role', meta)

from pgadmin.tools.user_management.PgAdminPermissions import AllPermissionTypes
op.execute(
role_table.update().where(role_table.c.name == 'User')
.values(permissions=",".join(AllPermissionTypes.list())))


def downgrade():
# pgAdmin only upgrades, downgrade not implemented.
Expand Down
2 changes: 2 additions & 0 deletions web/pgadmin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,8 @@ def get_locale():
app.config['SECURITY_MSG_INVALID_PASSWORD'] = \
(gettext("Incorrect username or password."), "error")
app.config['SECURITY_PASSWORD_LENGTH_MIN'] = config.PASSWORD_LENGTH_MIN
app.config['SECURITY_MSG_UNAUTHORIZED'] = \
(gettext("Unauthorised access, permission denied."), "error")

# Create database connection object and mailer
db.init_app(app)
Expand Down
4 changes: 3 additions & 1 deletion web/pgadmin/browser/server_groups/servers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from flask import render_template, request, make_response, jsonify, \
current_app, url_for, session
from flask_babel import gettext
from flask_security import current_user
from flask_security import current_user, permissions_required
from pgadmin.user_login_check import pga_login_required
from psycopg.conninfo import make_conninfo, conninfo_to_dict

Expand All @@ -24,6 +24,7 @@
from pgadmin.utils.crypto import encrypt, decrypt, pqencryptpassword
from pgadmin.utils.menu import MenuItem
from pgadmin.tools.sqleditor.utils.query_history import QueryHistory
from pgadmin.tools.user_management.PgAdminPermissions import AllPermissionTypes

import config
from config import PG_DEFAULT_DRIVER
Expand Down Expand Up @@ -1081,6 +1082,7 @@ def update_connection_string(manager, server):
display_conn_string = make_conninfo(**con_info_ord)
return display_conn_string

@permissions_required(AllPermissionTypes.object_register_server)
@pga_login_required
def create(self, gid):
"""Add a server node to the settings database"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import PGSchema from './schema.ui';
import { getNodePrivilegeRoleSchema } from '../../../../static/js/privilege.ui';
import { getNodeListByName } from '../../../../../../static/js/node_ajax';
import { AllPermissionTypes } from '../../../../../../static/js/constants';

define('pgadmin.node.schema', [
'sources/gettext', 'sources/url_for',
Expand Down Expand Up @@ -64,7 +65,8 @@ define('pgadmin.node.schema', [
},{
name: 'generate_erd', node: 'schema', module: this,
applies: ['object', 'context'], callback: 'generate_erd',
priority: 5, label: gettext('ERD For Schema')
priority: 5, label: gettext('ERD For Schema'),
permission: AllPermissionTypes.TOOLS_ERD_TOOL,
}]);
},
can_create_schema: function(node) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import { getNodeTableSchema } from './table.ui';
import _ from 'lodash';
import getApiInstance from '../../../../../../../../static/js/api_instance';
import { AllPermissionTypes } from '../../../../../../../static/js/constants';

define('pgadmin.node.table', [
'pgadmin.tables.js/enable_disable_triggers',
Expand Down Expand Up @@ -127,7 +128,8 @@ define('pgadmin.node.table', [
priority: 5, label: gettext('ERD For Table'),
enable: (_, item) => {
return !('catalog' in pgAdmin.Browser.tree.getTreeNodeHierarchy(item));
}
},
permission: AllPermissionTypes.TOOLS_ERD_TOOL,
}
]);
pgBrowser.Events.on(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import DatabaseSchema from './database.ui';
import { showServerPassword } from '../../../../../../static/js/Dialogs/index';
import _ from 'lodash';
import getApiInstance, { parseApiError } from '../../../../../../static/js/api_instance';
import { AllPermissionTypes } from '../../../../../static/js/constants';

define('pgadmin.node.database', [
'sources/gettext', 'sources/url_for',
Expand Down Expand Up @@ -122,7 +123,8 @@ define('pgadmin.node.database', [
priority: 5, label: gettext('ERD For Database'),
enable: (node) => {
return node.allowConn;
}
},
permission: AllPermissionTypes.TOOLS_ERD_TOOL,
}]);

_.bindAll(this, 'connection_lost');
Expand Down
7 changes: 4 additions & 3 deletions web/pgadmin/browser/server_groups/servers/static/js/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import ServerSchema from './server.ui';
import { showServerPassword, showChangeServerPassword, showNamedRestorePoint } from '../../../../../static/js/Dialogs/index';
import _ from 'lodash';
import getApiInstance, { parseApiError } from '../../../../../static/js/api_instance';
import { AllPermissionTypes } from '../../../../static/js/constants';

define('pgadmin.node.server', [
'sources/gettext', 'sources/url_for',
Expand Down Expand Up @@ -81,7 +82,7 @@ define('pgadmin.node.server', [
name: 'create_server_on_sg', node: 'server_group', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'register', priority: 1, label: gettext('Server...'),
data: {action: 'create'}, enable: 'canCreate',
data: {action: 'create'}, enable: 'canCreate', permission: AllPermissionTypes.OBJECT_REGISTER_SERVER
},{
name: 'disconnect_all_servers', node: 'server_group', module: this,
applies: ['object','context'], callback: 'disconnect_all_servers',
Expand All @@ -91,7 +92,7 @@ define('pgadmin.node.server', [
name: 'create_server', node: 'server', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'register', priority: 3, label: gettext('Server...'),
data: {action: 'create'}, enable: 'canCreate',
data: {action: 'create'}, enable: 'canCreate', permission: AllPermissionTypes.OBJECT_REGISTER_SERVER
},{
name: 'connect_server', node: 'server', module: this,
applies: ['object', 'context'], callback: 'connect_server',
Expand Down Expand Up @@ -167,7 +168,7 @@ define('pgadmin.node.server', [
name: 'copy_server', node: 'server', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
label: gettext('Copy Server...'), data: {action: 'copy'},
priority: 4,
priority: 4, permission: AllPermissionTypes.OBJECT_REGISTER_SERVER,
}]);

_.bindAll(this, 'connection_lost');
Expand Down
6 changes: 4 additions & 2 deletions web/pgadmin/browser/static/js/MainMenuFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import pgAdmin from 'sources/pgadmin';
import Menu, { MenuItem } from '../../../static/js/helpers/Menu';
import getApiInstance from '../../../static/js/api_instance';
import url_for from 'sources/url_for';
import withCheckPermission from './withCheckPermission';

const MAIN_MENUS = [
{ label: gettext('File'), name: 'file', id: 'mnu_file', index: 0, addSeprator: true, hasDynamicMenuItems: false },
Expand Down Expand Up @@ -71,7 +72,7 @@ export default class MainMenuFactory {
}

static createMenuItem(options) {
return new MenuItem({...options, callback: () => {
const callback = () => {
// Some callbacks registered in 'callbacks' check and call specifiec callback function
if (options.module && 'callbacks' in options.module && options.module.callbacks[options.callback]) {
options.module.callbacks[options.callback].apply(options.module, [options.data, pgAdmin.Browser.tree?.selected()]);
Expand All @@ -89,7 +90,8 @@ export default class MainMenuFactory {
pgAdmin.Browser.notifier.error(gettext('Error in opening window'));
});
}
}}, (menu, item)=> {
};
return new MenuItem({...options, callback: withCheckPermission(options, callback)}, (menu, item)=> {
pgAdmin.Browser.Events.trigger('pgadmin:enable-disable-menu-items', menu, item);
window.electronUI?.enableDisableMenuItems(menu?.serialize(), item?.serialize());
});
Expand Down
1 change: 1 addition & 0 deletions web/pgadmin/browser/static/js/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ define('pgadmin.browser', [
checked: _m.checked,
below: _m.below,
applies: _m.applies,
permission: _m.permission,
};
};

Expand Down
4 changes: 4 additions & 0 deletions web/pgadmin/browser/static/js/collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//
//////////////////////////////////////////////////////////////
import _ from 'lodash';
import { AllPermissionTypes } from './constants';

define([
'sources/gettext', 'sources/pgadmin',
Expand Down Expand Up @@ -53,13 +54,15 @@ define([
name: 'show_query_tool', node: this.type, module: this,
applies: ['context'], callback: 'show_query_tool',
priority: 998, label: gettext('Query Tool'),
permission: AllPermissionTypes.TOOLS_QUERY_TOOL,
}]);

// show search objects same as query tool
pgAdmin.Browser.add_menus([{
name: 'search_objects', node: this.type, module: this,
applies: ['context'], callback: 'show_search_objects',
priority: 997, label: gettext('Search Objects...'),
permission: AllPermissionTypes.TOOLS_SEARCH_OBJECTS,
}]);

// show psql tool same as query tool.
Expand All @@ -68,6 +71,7 @@ define([
name: 'show_psql_tool', node: this.type, module: this,
applies: ['context'], callback: 'show_psql_tool',
priority: 998, label: gettext('PSQL Tool'),
permission: AllPermissionTypes.TOOLS_PSQL_TOOL,
}]);
}
}
Expand Down
18 changes: 18 additions & 0 deletions web/pgadmin/browser/static/js/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,21 @@ export const WEEKDAYS = [
];

export const PGAGENT_MONTHDAYS = [...MONTHDAYS].concat([{label: gettext('Last day'), value: 'Last Day'}]);

export const AllPermissionTypes = {
OBJECT_REGISTER_SERVER: 'object_register_server',
TOOLS_ERD_TOOL: 'tools_erd_tool',
TOOLS_QUERY_TOOL: 'tools_query_tool',
TOOLS_DEBUGGER: 'tools_debugger',
TOOLS_PSQL_TOOL: 'tools_psql_tool',
TOOLS_BACKUP: 'tools_backup',
TOOLS_RESTORE: 'tools_restore',
TOOLS_IMPORT_EXPORT_DATA: 'tools_import_export_data',
TOOLS_IMPORT_EXPORT_SERVERS: 'tools_import_export_servers',
TOOLS_SEARCH_OBJECTS: 'tools_search_objects',
TOOLS_MAINTENANCE: 'tools_maintenance',
TOOLS_SCHEMA_DIFF: 'tools_schema_diff',
TOOLS_GRANT_WIZARD: 'tools_grant_wizard',
STORAGE_ADD_FOLDER: 'storage_add_folder',
STORAGE_REMOVE_FOLDER: 'storage_remove_folder'
};
Loading
Loading