From 40c592a9888b412b475e6f9f2f42c93940a93f13 Mon Sep 17 00:00:00 2001 From: Surinder Kumar Date: Wed, 13 Apr 2016 16:11:43 +0100 Subject: [PATCH] Grant wizard. --- web/pgadmin/browser/static/css/wizard.css | 143 +++ web/pgadmin/browser/static/js/node.ui.js | 3 +- web/pgadmin/browser/static/js/wizard.js | 233 ++++ web/pgadmin/static/css/overrides.css | 4 + web/pgadmin/tools/grant_wizard/__init__.py | 461 +++++++ .../grant_wizard/static/css/grant_wizard.css | 109 ++ .../templates/grant_wizard/js/grant_wizard.js | 1101 +++++++++++++++++ .../grant_wizard/pg/9.1_plus/acl.json | 30 + .../grant_wizard/pg/9.1_plus/sql/function.sql | 23 + .../pg/9.1_plus/sql/get_schemas.sql | 19 + .../pg/9.1_plus/sql/grant_function.sql | 10 + .../pg/9.1_plus/sql/grant_sequence.sql | 9 + .../pg/9.1_plus/sql/grant_table.sql | 9 + .../grant_wizard/pg/9.1_plus/sql/sequence.sql | 15 + .../grant_wizard/pg/9.1_plus/sql/table.sql | 19 + .../grant_wizard/pg/9.1_plus/sql/view.sql | 28 + .../grant_wizard/ppas/9.1_plus/acl.json | 30 + .../ppas/9.1_plus/sql/function.sql | 23 + .../ppas/9.1_plus/sql/get_schemas.sql | 19 + .../ppas/9.1_plus/sql/grant_function.sql | 10 + .../ppas/9.1_plus/sql/grant_sequence.sql | 9 + .../ppas/9.1_plus/sql/grant_table.sql | 9 + .../ppas/9.1_plus/sql/sequence.sql | 15 + .../grant_wizard/ppas/9.1_plus/sql/table.sql | 19 + .../grant_wizard/ppas/9.1_plus/sql/view.sql | 28 + 25 files changed, 2377 insertions(+), 1 deletion(-) create mode 100644 web/pgadmin/browser/static/css/wizard.css create mode 100644 web/pgadmin/browser/static/js/wizard.js create mode 100644 web/pgadmin/tools/grant_wizard/__init__.py create mode 100644 web/pgadmin/tools/grant_wizard/static/css/grant_wizard.css create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/js/grant_wizard.js create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/acl.json create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/function.sql create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/get_schemas.sql create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/grant_function.sql create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/grant_sequence.sql create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/grant_table.sql create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/sequence.sql create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/table.sql create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/view.sql create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/acl.json create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/function.sql create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/get_schemas.sql create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/grant_function.sql create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/grant_sequence.sql create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/grant_table.sql create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/sequence.sql create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/table.sql create mode 100644 web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/view.sql diff --git a/web/pgadmin/browser/static/css/wizard.css b/web/pgadmin/browser/static/css/wizard.css new file mode 100644 index 000000000..0be48dca5 --- /dev/null +++ b/web/pgadmin/browser/static/css/wizard.css @@ -0,0 +1,143 @@ +/** CSS for Wizard **/ +.pgadmin_grant_wizard_body .ajs-content { + padding-top: 0px !important; +} + +.wizard-header { + background: #428bca; + padding-left: 15px; + padding-bottom: 7px; + color: #fff; + font-size: 18px; + height: 76px; + line-height: 76px; + border-radius: 6px 6px 0 0; +} + +.wizard-header h3 { + font-size: 18px; + display: inline-block; +} + +.wizard_dlg { + float: left; + height: 100%; + width: 100%; +} + +.grant_wizard_container { + position: relative; + overflow: hidden; + width: 100%; + height: 100%; +} + +.grant_wizard_container .pgadmin-wizard { + width: 100%; + height: 100%; +} + +.grant_wizard_container .wizard-content { + position: relative; + padding: 0; + height: 78%; +} + +.grant_wizard_container .wizard-right-panel { + overflow-y: auto; + height: 100%; + top: 0; + right: 0; + position: absolute; + bottom: 0; +} + +.grant_wizard_container .wizard-left-panel { + position: absolute; + top: 0; + display: flex; + bottom: 0; + left: 0; + align-items: center; + justify-content: center; + right: 0; +} + +.grant_wizard_container .wizard-left-panel img { + width: 140px; +} + +.grant_wizard_container .wizard-right-panel_content { + height: 60%; +} + +.grant_wizard_container { + height: 100%; +} + +/* Wizard Footer CSS */ +.grant_wizard_container .footer { + position: absolute; + background: #fff; + border-top: 1px solid #ccc; + bottom: 0px; + height: 62px; + right: 0px; + padding-top: 22px; + border-radius: 0 0 6px 6px; + z-index: 10; +} + +/* Wizard Button CSS */ +.grant_wizard_container .wizard-buttons { + float: right; +} + +.grant_wizard_container .wizard-buttons button { + float: left; + padding: 7px 15.2px; + font-size: 14px; + margin: 0px 5px 0 0 !important; +} + +.grant_wizard_container .wizard-buttons button.wizard-next i.fa { + padding-left: 5px; +} + +.grant_wizard_container .wizard-buttons button.wizard-back i.fa, +.grant_wizard_container .wizard-buttons button.wizard-cancel i.fa { + padding-right: 5px; +} + +.grant_wizard_container .wizard-buttons .wizard-finish { + margin-right: 0 !important; +} + +/* Wizard Status bar CSS */ +.grant_wizard_container .wizard-description { + padding: 1.7em 0.1em; +} + +/* Error message css */ +.grant_wizard_container .error_msg_div { + display: block; + position: absolute; + bottom: 55px; + background: #fff; +} + +.grant_wizard_container .error_msg_div p { + background: #fff; + color: #b92c28; +} + +/* In wizard select2 dropdown doesn't + * popup because z-index of alertify + * wizard is greater than the z-index + * of select2 dropdown. To make select2 + * visible, set z-index of select2 + * higher value than wizard's + */ +.select2-container--open { + z-index: 10000; +} diff --git a/web/pgadmin/browser/static/js/node.ui.js b/web/pgadmin/browser/static/js/node.ui.js index c3968136e..6cd3d684d 100644 --- a/web/pgadmin/browser/static/js/node.ui.js +++ b/web/pgadmin/browser/static/js/node.ui.js @@ -280,7 +280,8 @@ function($, _, pgAdmin, Backbone, Backform, Alertify, Node) { return false; }; - while(p) { + // check if p is not empty + while(p && p.length > 0) { top = p.get(0).offsetTop + p.height(); p = p.parent(); if (hasScrollbar(p)) { diff --git a/web/pgadmin/browser/static/js/wizard.js b/web/pgadmin/browser/static/js/wizard.js new file mode 100644 index 000000000..383a31228 --- /dev/null +++ b/web/pgadmin/browser/static/js/wizard.js @@ -0,0 +1,233 @@ +define( + ['underscore', 'backbone', 'pgadmin', 'pgadmin.browser'], +function(_, Backbone, pgAdmin, pgBrowser) { + + pgBrowser = pgBrowser || pgAdmin.Browser || {}; + + /* Wizard individual Page Model */ + var WizardPage = pgBrowser.WizardPage = Backbone.Model.extend({ + defaults: { + id: undefined, /* Id */ + page_title: undefined, /* Page Title */ + view: undefined, /* A Backbone View */ + html: undefined, /* HTML tags to be rendered */ + image: undefined, /* Left hand side image */ + disable_prev: false, /* Previous Button Flag */ + disable_next: false, /* Next Button Flag */ + disable_cancel: false, /* Cancel Button Flag */ + show_progress_bar: '', + /* Callback for OnLoad */ + onLoad: function() { + return true; + }, + /* Callback for before Next */ + beforeNext: function() { + return true; + }, + onNext: function(){}, + onBefore: function() {}, + /* Callback for before Previous */ + beforePrev: function() { + return true; + } + } + }); + + var Wizard = pgBrowser.Wizard = Backbone.View.extend({ + options: { + title: 'Wizard', /* Main Wizard Title */ + image: 'left_panel.png', /* TODO:: We can use default image here */ + curr_page: 0, /* Current Page to Load */ + disable_next: false, + disable_prev: false, + disable_finish: false, + disable_cancel: false, + height: 400, + width: 650, + show_left_panel: true + }, + tmpl: _.template( + "
" + + "
" + + "
" + + "
" + + "

<%= this.options.title %> -" + + " <%= page_title %>

" + + "
" + + "
" + + "
" + + "
" + + " <% if(this.options.show_left_panel) { %>" + + "
" + + "
" + + " <% } %>" + + "
" + + " <% if( typeof show_description != 'undefined'){ %>" + + "
" + + " <%= show_description %>" + + "
" + + " <% } %>" + + "
" + + "
" + + "
" + + "
" + + "
" + + "

" + + "
" + + " " + + "
"), + events: { + "click button.wizard-next" : "nextPage", + "click button.wizard-back" : "prevPage", + "click button.wizard-cancel" : "onCancel", + "click button.wizard-finish" : "finishWizard", + }, + initialize: function(options) { + this.options = _.extend({}, this.options, options.options); + this.currPage = this.collection.at(this.options.curr_page).toJSON(); + }, + render: function() { + var data = this.currPage; + + /* Check Status of the buttons */ + this.options.disable_next = (this.options.disable_next ? true : this.evalASFunc(this.currPage.disable_next)); + this.options.disable_prev = (this.options.disable_prev ? true : this.evalASFunc(this.currPage.disable_prev)); + this.options.disable_cancel = (this.currPage.canCancel ? true : this.evalASFunc(this.currPage.disable_cancel)); + + that = this; + + /* HTML Content */ + if (data.html) { data.content = data.html; } + /* Backbone View */ + else if (data.view) { data.content = data.view.render().el;} + + $(this.el).html(this.tmpl(data)); + $(this.el).find(".wizard-right-panel_content").html(data.content); + + /* OnLoad Callback */ + this.onLoad(); + + return this; + }, + nextPage: function() { + this.options.curr_page.el = this.$el; + if (!this.beforeNext()) { return false; } + + page_id = this.onNext(); + + if (page_id ) { + this.currPage = this.collection.get(page_id).toJSON(); + this.options.curr_page = this.collection.indexOf(this.collection.get(page_id)); + } + else if (this.options.curr_page < (this.collection.length-1)) { + this.options.curr_page = this.options.curr_page + 1; + this.currPage = this.collection.at(this.options.curr_page).toJSON(); + } + + this.enableDisableNext(); + this.enableDisablePrev(); + + return this.render(); + }, + prevPage: function() { + if (!this.beforePrev()) { return false; } + + page_id = this.onPrev(); + + if (page_id){ + this.currPage = this.collection.get(page_id).toJSON(); + this.options.curr_page = this.collection.indexOf(this.collection.get(page_id)); + } + else if (this.options.curr_page > 0) { + this.options.curr_page = this.options.curr_page - 1; + this.currPage = this.collection.at(this.options.curr_page).toJSON(); + } + + this.enableDisableNext(); + this.enableDisablePrev(); + + return this.render(); + }, + finishWizard: function() { + this.onFinish(); + this.remove(); // Remove view from DOM + this.unbind(); // Unbind all local event bindings + delete this.$el; // Delete the jQuery wrapped object variable + delete this.el; // Delete the variable reference to this node + return true; + }, + enableDisableNext: function(disable) { + if (typeof(disable) != 'undefined') { + this.options.disable_next = disable; + } + else if (this.options.curr_page >= (this.collection.length-1)) { + this.options.disable_next = true; + } + else { + this.options.disable_next = false; + } + }, + enableDisablePrev: function(disable) { + if (typeof(disable) != 'undefined') { + this.options.disable_prev = disable; + } + else if (this.options.curr_page <= 0) { + this.options.disable_prev = true; + } + else { + this.options.disable_prev = false; + } + }, + beforeNext: function(){ + return this.evalASFunc(this.currPage.beforeNext); + }, + beforePrev: function(){ + return this.evalASFunc(this.currPage.beforePrev); + }, + onPrev: function(){ + return this.evalASFunc(this.currPage.onPrev); + }, + onNext: function(){ + return this.evalASFunc(this.currPage.onNext); + }, + onLoad: function() { + return this.evalASFunc(this.currPage.onLoad); + }, + onFinish: function() { + return true; + }, + onCancel: function() { + this.$el.remove(); + return true; + }, + evalASFunc: function(func, ctx) { + var self = this; + ctx = ctx || self.currPage; + + return (_.isFunction(func) ? func.apply(ctx, [self]) : func); + } + }); + + return pgBrowser; + +}); diff --git a/web/pgadmin/static/css/overrides.css b/web/pgadmin/static/css/overrides.css index 7ce0491f3..3843734f1 100755 --- a/web/pgadmin/static/css/overrides.css +++ b/web/pgadmin/static/css/overrides.css @@ -45,6 +45,10 @@ body { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAVElEQVQY05WQuQ0AMQgEB8lN0H9vdhnj4B7ZJwfcJkjsImBCpaIGMMYQIDNjNbe+Su9d1bsee5yM0+AbXMPfkHrdWNKv1ZVn2oJiw5OZ8eABiCrwCW8QwRIof5qAAAAAAElFTkSuQmCC) !important; } +.alertify .ajs-modal { + overflow: hidden; +} + /* iFrames should have no border */ iframe { border-width: 0; diff --git a/web/pgadmin/tools/grant_wizard/__init__.py b/web/pgadmin/tools/grant_wizard/__init__.py new file mode 100644 index 000000000..f53b979f6 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/__init__.py @@ -0,0 +1,461 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements Grant Wizard""" + +import simplejson as json +from flask import render_template, request, current_app +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_response as ajax_response, \ + make_json_response, internal_server_error +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from pgadmin.browser.server_groups.servers.utils import parse_priv_to_db +from pgadmin.utils import PgAdminModule +from flask import Response, url_for +from flask.ext.security import login_required +from urllib import unquote +from pgadmin.utils.ajax import precondition_required +from functools import wraps +from pgadmin.utils.preferences import Preferences + +# set template path for sql scripts +MODULE_NAME = 'grant_wizard' +server_info = {} + + +class GrantWizardModule(PgAdminModule): + """ + class GrantWizardModule(Object): + + It is a wizard which inherits PgAdminModule + class and define methods to load its own + javascript file. + + LABEL = gettext('Browser') + """ + def get_own_stylesheets(self): + """ + Returns: + list: the stylesheets used by this module. + """ + stylesheets = [ + url_for('browser.static', filename='css/wizard.css'), + url_for('grant_wizard.static', filename='css/grant_wizard.css') + ] + return stylesheets + + def get_own_javascripts(self): + """" + Returns: + list: js files used by this module + """ + scripts = [] + scripts.append({ + 'name': 'pgadmin.tools.grant_wizard', + 'path': url_for('grant_wizard.index') + 'grant_wizard', + 'when': None + }) + scripts.append({ + 'name': 'pgadmin.browser.wizard', + 'path': url_for('browser.static', filename='js/wizard'), + 'when': None + }) + return scripts + + def show_system_objects(self): + """ + return system preference objects + """ + return self.pref_show_system_objects + + def register_preferences(self): + """ + Get show_system_objects preference + """ + self.browser_preference = Preferences.module('browser') + self.pref_show_system_objects = self.browser_preference.preference( + 'show_system_objects' + ) + +# Create blueprint for GrantWizardModule class +blueprint = GrantWizardModule( + MODULE_NAME, __name__, static_url_path='') + + +def check_precondition(f): + """ + This function will behave as a decorator which will checks + database connection before running view, it will also attaches + manager,conn & template_path properties to instance of the method. + + Assumptions: + This function will always be used as decorator of a class method. + """ + @wraps(f) + def wrap(*args, **kwargs): + # Here args[0] will hold self & kwargs will hold gid,sid,did + + server_info.clear() + server_info['manager'] = get_driver( + PG_DEFAULT_DRIVER).connection_manager( + kwargs['sid'] + ) + server_info['conn'] = server_info['manager'].connection( + did=kwargs['did'] + ) + # If DB not connected then return error to browser + if not server_info['conn'].connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + # Set template path for sql scripts + server_info['server_type'] = server_info['manager'].server_type + server_info['version'] = server_info['manager'].version + if server_info['server_type'] == 'pg': + server_info['template_path'] = 'grant_wizard/pg/9.1_plus' + elif server_info['server_type'] == 'ppas': + server_info['template_path'] = 'grant_wizard/ppas/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + +@blueprint.route("/") +@login_required +def index(): + pass + + +@blueprint.route("/grant_wizard.js") +@login_required +def script(): + """render own javascript""" + return Response(response=render_template( + "grant_wizard/js/grant_wizard.js", _=gettext), + status=200, + mimetype="application/javascript") + + +@blueprint.route( + '/acl////', methods=('GET', 'POST')) +@login_required +@check_precondition +def acl_list(gid, sid, did): + """render list of acls""" + server_prop = server_info + return Response(response=render_template( + server_prop['template_path']+"/acl.json", _=gettext), + status=200, + mimetype="application/json") + + +@blueprint.route( + '/properties///' + '///', + methods=('GET', 'POST')) +@login_required +@check_precondition +def properties(gid, sid, did, node_id, node_type): + """It fetches the properties of object types + and render into selection page of wizard + """ + + # unquote encoded url parameter + node_type = unquote(node_type) + + server_prop = server_info + + res_data = [] + manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid) + conn = manager.connection(did=did) + + node_types = [] + nspname = '' + show_sysobj = blueprint.show_system_objects().get() + if node_type == 'database': + + # Fetch list of schemas + # Get sys_obj_values and get list of schemas + ntype = 'schema' + SQL = render_template("/".join( + [server_prop['template_path'], '/sql/get_schemas.sql']), + show_sysobj=show_sysobj) + status, res = conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + node_types = res['rows'] + else: + SQL = render_template("/".join( + [server_prop['template_path'], '/sql/get_schemas.sql']), + nspid=node_id, show_sysobj=False) + status, res = conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + node_types = res['rows'] + ntype = node_type + nspname = node_types[0]['name'] + + for row in node_types: + if 'oid' in row: + node_id = row['oid'] + nspname = row['name'] + + # Fetch functions against schema + if ntype in ['schema', 'function']: + SQL = render_template("/".join( + [server_prop['template_path'], '/sql/function.sql']), + node_id=node_id, nspname=nspname, type='function') + + status, res = conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + + res_data.extend(res['rows']) + + # Fetch trigger functions + if ntype in ['schema', 'trigger_function']: + SQL = render_template("/".join( + [server_prop['template_path'], '/sql/function.sql']), + node_id=node_id, nspname=nspname, type='trigger_function') + status, res = conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + + res_data.extend(res['rows']) + + # Fetch Sequences against schema + if ntype in ['schema', 'sequence']: + SQL = render_template("/".join( + [server_prop['template_path'], '/sql/sequence.sql']), + node_id=node_id, nspname=nspname) + + status, res = conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + res_data.extend(res['rows']) + + # Fetch Tables against schema + if ntype in ['schema', 'table']: + SQL = render_template("/".join( + [server_prop['template_path'], '/sql/table.sql']), + node_id=node_id, nspname=nspname) + + status, res = conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + res_data.extend(res['rows']) + + # Fetch Views against schema + if ntype in ['schema', 'view']: + SQL = render_template("/".join( + [server_prop['template_path'], '/sql/view.sql']), + node_id=node_id, nspname=nspname) + + status, res = conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + res_data.extend(res['rows']) + + return ajax_response( + response=res_data, + status=200 + ) + + +@blueprint.route( + '/msql////', + methods=('GET', 'POST')) +@login_required +@check_precondition +def msql(gid, sid, did): + """ + This function will return modified SQL + """ + + server_prop = server_info + data = {} + for k, v in request.args.items(): + try: + data[k] = json.loads(v) + except ValueError: + data[k] = v + + # Form db connection + manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid) + conn = manager.connection(did=did) + + acls = [] + try: + acls = render_template( + "/".join([server_prop['template_path'], '/acl.json']) + ) + acls = json.loads(acls) + except Exception as e: + current_app.logger.exception(e) + + try: + + # Parse privileges + data['priv'] = {} + if 'acl' in data: + # Get function acls + data['priv']['function'] = parse_priv_to_db( + data['acl'], + acls['function']['acl']) + + data['priv']['sequence'] = parse_priv_to_db( + data['acl'], + acls['sequence']['acl']) + + data['priv']['table'] = parse_priv_to_db( + data['acl'], + acls['table']['acl']) + + # Pass database objects and get SQL for privileges + SQL_data = '' + data_func = {} + data_func['objects'] = data['objects'] + data_func['priv'] = data['priv']['function'] + SQL = render_template( + "/".join([server_prop['template_path'], + '/sql/grant_function.sql']), + data=data_func, conn=conn) + if SQL and SQL.strip('\n') != '': + SQL_data += SQL + + data_seq = {} + data_seq['objects'] = data['objects'] + data_seq['priv'] = data['priv']['sequence'] + SQL = render_template( + "/".join([server_prop['template_path'], + '/sql/grant_sequence.sql']), + data=data_seq, conn=conn) + if SQL and SQL.strip('\n') != '': + SQL_data += SQL + + data_table = {} + data_table['objects'] = data['objects'] + data_table['priv'] = data['priv']['table'] + SQL = render_template( + "/".join([server_prop['template_path'], '/sql/grant_table.sql']), + data=data_table, conn=conn) + if SQL and SQL.strip('\n') != '': + SQL_data += SQL + + res = {'data': SQL_data} + + return ajax_response( + response=res, + status=200 + ) + + except Exception as e: + return make_json_response( + status=410, + success=0, + errormsg=e.message + ) + + +@blueprint.route( + '/save////', + methods=('GET', 'POST')) +@login_required +@check_precondition +def save(gid, sid, did): + """ + This function will apply the privileges to the selected + Database Objects + """ + server_prop = server_info + data = request.form if request.form else json.loads(request.data.decode()) + + # Form db connection and we use conn to execute sql + manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid) + conn = manager.connection(did=did) + + acls = [] + try: + acls = render_template( + "/".join([server_prop['template_path'], 'acl.json']), + ) + acls = json.loads(acls) + except Exception as e: + current_app.logger.exception(e) + + try: + + # Parse privileges + data['priv'] = {} + if 'acl' in data: + # Get function acls + data['priv']['function'] = parse_priv_to_db( + data['acl'], + acls['function']['acl']) + + data['priv']['sequence'] = parse_priv_to_db( + data['acl'], + acls['sequence']['acl']) + + data['priv']['table'] = parse_priv_to_db( + data['acl'], + acls['table']['acl']) + + # Pass database objects and get SQL for privileges + # Pass database objects and get SQL for privileges + SQL_data = '' + data_func = {} + data_func['objects'] = data['objects'] + data_func['priv'] = data['priv']['function'] + SQL = render_template( + "/".join([server_prop['template_path'], + '/sql/grant_function.sql']), + data=data_func, conn=conn) + if SQL and SQL.strip('\n') != '': + SQL_data += SQL + + data_seq = {} + data_seq['objects'] = data['objects'] + data_seq['priv'] = data['priv']['sequence'] + SQL = render_template( + "/".join([server_prop['template_path'], + '/sql/grant_sequence.sql']), + data=data_seq, conn=conn) + if SQL and SQL.strip('\n') != '': + SQL_data += SQL + + data_table = {} + data_table['objects'] = data['objects'] + data_table['priv'] = data['priv']['table'] + SQL = render_template( + "/".join([server_prop['template_path'], '/sql/grant_table.sql']), + data=data_table, conn=conn) + if SQL and SQL.strip('\n') != '': + SQL_data += SQL + + status, res = conn.execute_dict(SQL_data) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Privileges applied" + ) + + except Exception as e: + return internal_server_error(errormsg=e.message) diff --git a/web/pgadmin/tools/grant_wizard/static/css/grant_wizard.css b/web/pgadmin/tools/grant_wizard/static/css/grant_wizard.css new file mode 100644 index 000000000..18240e9a6 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/static/css/grant_wizard.css @@ -0,0 +1,109 @@ +/** Grant Wizard CSS **/ + +/** + CSS to make db object type table + fixed so that tbody content may + scroll + */ + +.db_objects_container { + height: 100%; +} + +.object_type_table { + display: inline-block; + height: 100%; + border: 0 !important; +} + +.object_type_table thead tr { + position: relative; + display: block; + width: 100%; +} + +.object_type_table tbody { + display: block; + overflow: scroll; + border: 1px solid #ddd; + width: 100%; + min-height: 100%; + max-height: 66%; + height: 66%; +} + +.object_type_table tbody tr { + display: table; + max-width: 100%; + width: 100%; +} + +.object_type_table tbody tr td { + background-position: 4px 4px; + border-radius: 0; +} + +.object_type_table tbody tr td:nth-child(1), +.object_type_table thead tr th:nth-child(1) { + width: 28px; + min-width: 28px; +} + +.object_type_table tbody tr td:nth-child(2) { + width: 161px; + min-width: 161px; + max-width: 161px; +} + +.object_type_table thead tr th:nth-child(2) { + width: 161px; + min-width: 161px; + max-width: 161px; +} + +.object_type_table tbody tr td:nth-child(3) { + width: 109px; + min-width: 109px; + max-width: 109px; +} + +.object_type_table thead tr th:nth-child(3) { + width: 109px; + min-width: 109px; + max-width: 109px; +} + +.object_type_table thead tr th:nth-child(4) { + width: 100%; +} + +.object_type_table tbody tr td:nth-child(4) { + width: 100%; + max-width: 100%; +} + +/** Override Backgrid filter CSS **/ +.db_objects_container .backgrid-filter.form-search { + float: left; + margin: 0px 5px 5px 0; +} + +/** Custom styling for Codemirror field **/ +.wizard-right-panel_content { + border: 1px solide #ccc; +} + +.wizard-right-panel_content .CodeMirror { + border: 1px solid #ccc; + height: 285px !important; + min-height: 285px !important; +} + +.wizard-right-panel_content .CodeMirror-linenumber { + background: #F7F7F7; + border-right: 1px solid #DDDDDD; +} + +.wizard-right-panel_content .CodeMirror-gutters { + min-height: 285px !important; +} diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/js/grant_wizard.js b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/js/grant_wizard.js new file mode 100644 index 000000000..2acb162b5 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/js/grant_wizard.js @@ -0,0 +1,1101 @@ +define([ + 'jquery', 'underscore', 'underscore.string', 'alertify', + 'pgadmin.browser', 'backbone', 'backgrid', 'pgadmin.browser.node', + 'backgrid.select.all', 'backgrid.filter', 'pgadmin.browser.server.privilege', + 'pgadmin.browser.wizard', + ], + + // This defines Grant Wizard dialog + function($, _, S, alertify, pgBrowser, Backbone, Backgrid, pgNode) { + + // if module is already initialized, refer to that. + if (pgBrowser.GrantWizard) { + return pgBrowser.GrantWizard; + } + + /** + It is sub model for field "Objects". It has fields + for database object types such as Schemas, Views and + Sequence etc. + */ + var DatabaseObjectModel = pgNode.Model.extend({ + defaults: { + selected: false, + icon: 'icon-unknown', + name: undefined, + name_with_args: undefined, + nspname: undefined, + proargs: undefined, + object_type: undefined, + object_id: undefined + }, + idAttribute: 'object_id', // to uniquely identify a model object + toJSON: function(obj) { + var d = pgNode.Model.prototype.toJSON.apply(this); + delete d.icon; + return d; + }, + parse: function(res) { + + // Create unique object id + res.object_id = res.name_with_args; + + // create name with args if its object is function + if(!_.isUndefined(res.object_type) && (res.object_type == 'Function' || + res.object_type == 'Trigger Function')) + res.name_with_args = res.name+'('+(typeof(res.proargs) != 'undefined' ? res.proargs : '')+')'; + else + res.name_with_args = res.name; + + return res; + }, + + validate: function() { + + /* + * Triggers error messages for object types "selected" + * if it is empty/undefined/null + */ + var err = {}, + errmsg, + node = this.get('objects').toJSON(); + if (_.isEmpty(node)) { + err['selected'] = '{{ _("Please select any database object type") }}'; + errmsg = errmsg || err['selected']; + this.errorModel.set('selected', errmsg); + return errmsg; + } else { + this.errorModel.unset('selected'); + } + return null; + } + }); + + // Define columns for the Db Object Types grid + var columns = [{ + name: "selected", + + /* + Override render method of Backgrid.Extension.SelectRowCell + class. It has an issue: It doesn't mark rows checked if we move to next + page and then go back to previous page. but it must show. + so we handle this case by overriding the render method. + */ + cell: Backgrid.Extension.SelectRowCell.extend({ + render: function() { + + // Use the Backform Control's render function + Backgrid.Extension.SelectRowCell.prototype.render.apply(this, arguments); + + var col = this.column.get('name'); + if (this.model && this.model.has(col)) { + if (this.model.get(col)) { + this.checkbox().prop("checked", true); + this.$el.parent().toggleClass("selected", true); + this.model.trigger("backgrid:selected", this.model, true); + } + } + return this; + } + }), + + headerCell: "select-all", + + },{ + name: "object_type", + label: "Object Type", + editable: false, + cell: Backgrid.Cell.extend({ + render: function() { + + // Override render to add icon to Db Object column + Backgrid.Cell.prototype.render.apply(this, arguments); + this.$el.addClass(this.model.get('icon')).css({"padding-left": "24px"}); + + return this; + } + }) + },{ + name: "nspname", + label: "Schema", + cell: "string", + editable: false + },{ + name: "name_with_args", + label: "Name", + cell: "string", + editable: false + }]; + + // Create an Object GrantWizard of pgBrowser class + pgBrowser.GrantWizard = { + init: function() { + if (this.initialized) + return; + + this.initialized = true; + + // Define list of nodes on which grant wizard context menu option appears + var supported_nodes = [ + 'schema', 'coll-function', 'coll-sequence', + 'coll-table', 'coll-view', + 'coll-materialized_view', 'database' + ], + + /** + Enable/disable grantwizard menu in tools based + on node selected + if selected node is present in supported_nodes, + menu will be enabled otherwise disabled. + Also, hide it for system view in catalogs + */ + menu_enabled = function(itemData, item, data) { + var t = pgBrowser.tree, i = item, d = itemData; + var parent_item = t.hasParent(i) ? t.parent(i): null, + parent_data = parent_item ? t.itemData(parent_item) : null; + if(!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data)) + return ((_.indexOf(supported_nodes, d._type) !== -1 && parent_data._type != 'catalog') ? true: false); + else + return false; + }; + + // Define the nodes on which the menus to be appear + var menus = [{ + name: 'grant_wizard_schema', module: this, + applies: ['tools'], callback: 'start_grant_wizard', + priority: 10, label: '{{_("Grant Wizard...") }}', + icon: 'wcTabIcon', enable: menu_enabled + }]; + + // Add supported menus into the menus list + for (var idx = 0; idx < supported_nodes.length; idx++) { + menus.push({ + name: 'grant_wizard_schema_context_' + supported_nodes[idx], + node: supported_nodes[idx], module: this, + applies: ['context'], callback: 'start_grant_wizard', + priority: 10, label: '{{_("Grant Wizard...") }}', + icon: 'wcTabIcon', enable: menu_enabled + }); + } + pgAdmin.Browser.add_menus(menus); + + return this; + }, + + // Callback to draw Wizard Dialog + start_grant_wizard: function(action, item) { + + // Declare Wizard dialog + if (!alertify.wizardDialog) { + alertify.dialog('wizardDialog', function factory() { + + // Generate wizard main container + var $container = $("
"); + + return { + main: function(title) { + this.set('title', title); + }, + setup:function() { + return { + + // Set options for dialog + options: { + frameless: true, + resizable: true, + autoReset: false, + maximizable: false, + closableByDimmer: false + } + }; + }, + hooks:{ + onshow: function() { + + // Set dimensions for wizard + this.elements.dialog.style.width = '100%'; + this.elements.dialog.style.height = '68%'; + + // Add pgadmin_grant_wizard_body class to dialog + $(this.elements.body).addClass('pgadmin_grant_wizard_body'); + }, + }, + + /** + Returns a Paginator Class Object which is again to be rendered + + @class {Backgrid.Extension.Paginator} + @param {Backbone.Collection} coll - from which data is fetched + @return {Object} paginator + */ + DbPaginator: function(coll){ + var paginator = this.paginator = new Backgrid.Extension.Paginator({ + collection: coll, + windowSize: 8 + }); + return paginator; + }, + + /** + Create new Filter which will filter the + rendered grid for Select Type Tabular Data + @param {Backbone.PageableCollection} coll + */ + DbObjectFilter: function(coll){ + var clientSideFilter = this.clientSideFilter = new Backgrid.Extension.ClientSideFilter({ + collection: coll, + placeholder: _('Search by object type or name'), + + // The model fields to search for matches + fields: ['object_type', 'name'], + + // How long to wait after typing has stopped before searching can start + wait: 150 + }); + return clientSideFilter; + }, + + //Enable Disable Next button of PrivilegePage + updateButtons: function(modified){ + if(!modified) + $('.wizard-next').prop('disabled', true); + else + $('.wizard-next').prop('disabled', false); + }, + + /** + Callback called when an errorModel is set + with invalid value and errormsg is set into + status bar element and next button is disabled + */ + onSessionInvalid: function(msg) { + $('.error_msg_div p').html(msg).removeClass("hide"); + + // Enable disable Next button + this.updateButtons(false); + return true; + }, + + /** + Callback called when anything is set into model + thus hide error msg element and enable next button + status bar element and next button is disabled + */ + onSessionValidated: function(sessHasChanged) { + $('.error_msg_div p').empty().addClass("hide"); + + // Enable disable Next button + this.updateButtons(sessHasChanged); + }, + + /** + Remove/Delete objects, attributes + in wizard on wizard close or finish + to reclaim memory + */ + releaseObjects: function(){ + var self = this; + + if(!_.isUndefined(self.dbObjectFilter)) { + self.dbObjectFilter.remove(); + self.dbObjectFilter = undefined; + } + + if(!_.isUndefined(self.clientSideFilter)) { + self.clientSideFilter.remove(); + self.clientSideFilter = undefined; + } + + // clear object priv array + if(!_.isNull(self.obj_priv) && + !_.isUndefined(self.obj_priv)) { + self.obj_priv = []; + delete self.obj_priv; + } + + // Delete Wizard Pages, clear model and cleanup view + if(!_.isUndefined(self.dbObjectTypePage) && + !_.isNull(self.dbObjectTypePage)) { + if(!_.isUndefined(self.dbObjectTypePage.get('model')) && + !_.isNull(self.dbObjectTypePage.get('model'))) { + self.dbObjectTypePage.get('model').clear(); + self.dbObjectTypePage.get('view').cleanup(); + self.dbObjectTypePage = undefined; + } + } + + if(!_.isUndefined(self.privilegePage) && + !_.isNull(self.privilegePage)) { + if(!_.isUndefined(self.privilegePage.get('model')) && + !_.isNull(self.privilegePage.get('model'))) { + self.privilegePage.get('model').clear(); + self.privilegePage.get('view').cleanup(); + self.privilegePage = undefined; + } + } + + if(!_.isUndefined(self.reviewSQLPage) && + !_.isNull(self.reviewSQLPage)) { + if(!_.isUndefined(self.reviewSQLPage.get('model')) && + !_.isNull(self.reviewSQLPage.get('model'))) { + self.reviewSQLPage.get('model').clear(); + self.reviewSQLPage = undefined; + } + } + + // Remove Sql control + if (!_.isUndefined(self.sqlControl)) { + self.sqlControl.remove(); + } + + // Clear privModel + if(!_.isNull(self.privModel) && + !_.isUndefined(self.privModel)) { + self.privModel.clear(); + } + + // Remove collection containing db object data + if(!_.isNull(self.coll) && + !_.isUndefined(self.coll)) { + self.coll.reset(); + self.coll = undefined; + } + // Delete Wizard + if(!_.isNull(self.wizard) && + !_.isUndefined(self.wizard)) { + self.wizard.collection.reset(); + self.wizard.curr_page = undefined; + } + + }, + + /** + Every time a wizard is opened, this function + is called everytime. It has Wizard Pages which + are rendered by the Wizard Class: + + @class {pgBrowser.WizardPage} dbObjectType1 - This page + @extends {Backbone.Model} + renders a grid of Database Object Types such as + Schemas, Views and Sequences etc. + + @class {pgBrowser.WizardPage} WizardPage2 - This page + @extends {Backbone.Model} + adds Privilege Control which provides grant privileges + such as "Create, Insert, Delete, Update" so on the + database objects selected on Wizard Pages. + + @class {pgBrowser.WizardPage} WizardPage3 - This page + displays the generated GRANT SQL query for the Db + objects selected with the specific privileges added to it. + @extends {Backbone.Model} + + @class {Backbone.Collection} WizardCollection - It is the + collection of wizard pages + + @class {pgBrowser.Wizard} wizard - Its task is: + - Create a Wizard + - Add Buttons, Callbacks to it. + - Render WizardPages + @extends {Backbone.View} + + */ + build: function() { + this.elements.content.appendChild($container.get(0)); + }, + + //Returns list of Acls defined for nodes + get_json_data: function(gid, sid, did) { + var url = "{{ url_for('grant_wizard.index') }}" + "acl/" + + S('%s/%s/%s/').sprintf( + encodeURI(gid), encodeURI(sid), encodeURI(did)).value(); + return $.ajax({ + async: false, + url: url, + dataType: 'jsonp' + }); + + }, + prepare:function() { + + $container.empty().append("
"); + + // Define el for wizard view + var el = $('.grant_wizard_container'); + + // Extract the data from the selected tree node + var t = pgBrowser.tree, + i = t.selected(), + d = this.d = i && i.length == 1 ? t.itemData(i) : undefined, + info = this.info = pgBrowser.Node.getTreeNodeHierarchy(i), + icon = d.icon; + + /** + Generate a URL using: + gid, did, sid(server id), node_id(node id), + node_(node name), node_type(node type) + and pass it to collection which will fetch Object Type properties. + */ + var gid = info['server-group']._id, + sid = info.server._id, + did = info.database._id, + node_id = d._id, + + /** + get node name only. used in mapping with object types defined + in allowed_acl.json + */ + node_type = d._type.replace('coll-', '').replace('materialized_', ''), + node_label = d.label; + + // Fetch privileges specific to nodes + var json_data = this.get_json_data(gid, sid, did); + var privDict = JSON.parse(json_data.responseText); + + // Collection url to fetch database object types for objects field + var baseUrl = "{{ url_for('grant_wizard.index') }}" + "properties/" + + S('%s/%s/%s/%s/%s/').sprintf( + encodeURI(gid), encodeURI(sid), encodeURI(did), + encodeURI(node_id), encodeURI(node_type)).value(); + + // Model's save url + saveUrl = "{{ url_for('grant_wizard.index') }}" + "save/" + + S('%s/%s/%s/').sprintf( + encodeURI(gid), encodeURI(sid), + encodeURI(did)).value(), + + // generate encoded url based on wizard type + msql_url = this.msql_url = "/grant_wizard/msql/"+ + S('%s/%s/%s/').sprintf( + encodeURI(gid), encodeURI(sid), + encodeURI(did)).value(), + + Coll = Backbone.Collection.extend({ + model: DatabaseObjectModel, + url: baseUrl + }), + + // Create instances of collection and filter + coll = this.coll = new Coll(), + + coll.comparator = function(model) { + return model.get('object_type'); + } + + coll.sort(); + dbObjectFilter = this.dbObjectFilter = this.DbObjectFilter(coll); + + /** + privArray holds objects selected which further helps + in creating privileges Model + */ + var self = this; + self.privArray = []; + + /** + Override backgrid listener "backgrid:selected" to + Add/Remove model to/from objects collection + */ + coll.on('backgrid:selected', function(model, selected) { + model.set('selected', selected); + + var object_type = model.get('object_type'); + switch (object_type) + { + case 'Function': + object_type = 'function'; + break; + case 'Trigger Function': + object_type = 'function'; + break; + case 'Table': + object_type = 'table'; + break; + case 'Sequence': + object_type = 'sequence'; + break; + case 'View': + object_type = 'table'; + break; + } + + /** + if a row (checkbox) is checked, add that model + into collection, when unchecked remove it from + model. + + Also push/pop object type in/from privArray + */ + if(selected) { + if(_.indexOf(self.privArray, object_type) == -1) + self.privArray.push(object_type); + newModel.get('objects').add(model, { silent: true }); + } + else { + var idx = self.privArray.indexOf(object_type); + if(idx !=-1) + self.privArray.splice(idx, 1); + newModel.get('objects').remove(model); + } + + // validate model on checkbox check/uncheck + var msg = model.validate.call(newModel); + + /** + If no object type is selected, set error msg + and disable next button, else enable next button + */ + if(msg) + self.onSessionInvalid.call(self, msg); + else + self.onSessionValidated.call(self, true); + }); + + /** + It is the main model with schema defined + Every time a new wizard is opened, + a new model should create. + */ + var GrantWizardModel = pgNode.Model.extend({ + defaults: { + objects: undefined, + acl: undefined + }, + schema: [ + { + id: 'objects', label: '{{ _("Objects") }}', model: DatabaseObjectModel, + type: 'collection', group: 'Objects' + }, + { + id: 'acl', label: '{{ _("Privileges") }}', + model: pgAdmin.Browser.Node.PrivilegeRoleModel, + type: 'collection', canAdd: true, + canDelete: true, control: 'unique-col-collection' + } + ], + urlRoot: saveUrl + }); + + /** + Create instance of GrantWizard Model, provide urlRoot + node_info object, Generate fields objects + */ + var newModel = new GrantWizardModel({}, { node_info: info }); + + /** + Fetch data from server and set into grid + and show/hide progress bar + */ + $('.wizard-progress-bar p').show(); + + coll.fetch({ + success: function(collection, data) { + $('.wizard-progress-bar p').html(''); + }, + reset: true + }, this); + + ////////////////////////////////////////////////////////////////////// + // // + // Wizard Page for Db Object Type // + // // + ////////////////////////////////////////////////////////////////////// + + /** + Create wizard page. It renders a grid of + Database Object Types such as + Schemas, Views and Sequences etc. + Set default values + */ + var dbObjectTypePage = self.dbObjectTypePage = new pgBrowser.WizardPage({ + id: 1, + page_title: _('Object Selection (step 1 of 3)'), + disable_prev: true, + disable_next: true, + show_description: _('Please select objects from the list below.'), + show_progress_bar: _('Please wait while fetching records...'), + model: newModel, + view: new (function() { + + // Set page Instance + var pageView = this; + + _.extend(pageView, { + + // Remove grid if it is before render + cleanup: function() { + if (this.grid) { + this.grid.remove(); + delete this.grid; + this.grid = null; + } + + // Remove grid element if exists + if (this.el) { + $(this.el).remove(); + delete this.el; + } + }, + + // Delete grid before render + grid: null, + + render: function() { + + // Create a grid container + var gridBody = + $('
'); + + // Remove grid if exits before render + if (this.grid) { + this.cleanup(); + } + + // Initialize a new Grid instance + this.grid = new Backgrid.Grid({ + columns: _.clone(columns), + collection: coll, + className: "backgrid table-bordered object_type_table" + }); + + // Render selection Type grid and paginator + gridBody.append( this.grid.render().$el); + + // Render Search Filter + gridBody.prepend( + self.clientSideFilter.render().el); + + // Assign gridBody content to page element + this.el = gridBody; + + /** + Fetch selected models from collection and + make rows checked in grid + */ + newModel.get('objects').each(function(m) { + var model = coll.get(m.get('object_id')); + if (model) { + coll.trigger('backgrid:selected', model, true); + } + }); + + // Refresh grid to re render rows. + coll.trigger('backgrid:refresh'); + + return this; + } + }); + }), + + beforeNext: function(obj){ + var self = this; + obj.options.disable_next = true; + + /** + Enable/Disable next button of privilegePage if objects + are present in model + */ + if(!_.isNull(newModel.get('acl')) && + !_.isUndefined(newModel.get('acl'))) { + if(newModel.get('acl').length > 0) + obj.collection.at(1).set('disable_next', false); + } + + // Clean the view + if (self.view) { + self.view.cleanup(); + delete self.view; + self.view = null; + } + return true; + }, + + }); + + ////////////////////////////////////////////////////////////////////// + // // + // Wizard Page for Privilege Control // + // // + ////////////////////////////////////////////////////////////////////// + + // Wizard for Privelege control + var privilegePage = self.privilegePage = new pgBrowser.WizardPage({ + id: 2, + page_title: _('Privileges Selection (step 2 of 3)'), + show_description: _('Please select privileges for the selected objects.'), + disable_next: true, + model: newModel, + + // Create a view function object + view: new (function() { + var pageView = this; + _.extend(pageView, { + + // Render Privelege control to generate its html markup + render: function() { + + var obj_priv = []; + self.privArray = _.uniq(self.privArray); + _.each(self.privArray, function(priv){ + self.obj_priv = obj_priv = _.union(obj_priv , privDict[priv].acl); + }); + + /** + Define PrivModel and its instance. + Privileges array is generated based on + the type of nodes selected. + */ + var privModel = self.privModel; + var PrivModel = pgNode.Model.extend({ + defaults: { + acl: undefined + }, + schema: [ + { + id: 'acl', label: '{{ _("Privileges") }}', + model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend({ + + // privileges are selected based on node clicked + privileges: obj_priv + }), uniqueCol : ['grantee', 'grantor'], editable: true, + type: 'collection', canAdd: true, + canDelete: true, control: 'unique-col-collection' + } + ] + }); + + /** + When privelege control is re-rendered, in order to + render privileges based on object type selected, + delete privileges from privModel which are now not + present in object privileges array(object_priv) + */ + var data = {}; + if (privModel) { + data = privModel.toJSON(); + var rolePrivs = data['acl']; + + for (var idx in rolePrivs) { + var rolePriv = (rolePrivs[idx])['privileges'], + removeIdx = [], j; + + for (j in rolePriv) { + var p = rolePriv[j]; + if (_.indexOf(obj_priv, p['privilege_type']) == -1) { + removeIdx.push(j); + } + } + + for (j in removeIdx) { + rolePriv.splice(j, 1); + } + } + } + + // Instantiate privModel + privModel = self.privModel = new PrivModel(data, { node_info: self.info }); + + /* + To track changes into model, start new session + and Add event listener for privileges control + */ + self.privModel.startNewSession(); + self.privModel.on('pgadmin-session:valid', self.onSessionValidated.bind(self)); + self.privModel.on('pgadmin-session:invalid', self.onSessionInvalid.bind(self)); + + /** + Create Field Object which has properties like + node_data, node_info which is required for rendering + Privilege control + */ + var fields = Backform.generateViewSchema( + self.info, self.privModel, 'create', self.d._type, self.d + ); + var privilegesField = new Backform.Field(fields[0].fields[0]); + + this.privControl = new (privilegesField.get('control')) ({ + field: privilegesField, + model: self.privModel + }); + + return {el: this.privControl.render().$el}; + }, + + // Remove the privilege control + cleanup: function() { + if (this.privControl) { + this.privControl.remove(); + delete this.privControl; + this.privControl = null; + } + } + }); + }), + + beforePrev: function(wizardObj) { + + // Remove the privilege control + if (this.view) { + this.view.cleanup(); + delete this.view; + this.view = null; + } + + /** + Enable/Disable next button of DbObjectType page if objects + are present in model + */ + var objectsModel = newModel.get('objects'); + + if(!_.isUndefined(objectsModel) && !_.isEmpty(objectsModel) && + objectsModel.length > 0) { + wizardObj.collection.at(0).set('disable_next', false); + + // Don't show progress bar + wizardObj.collection.at(0).set('show_progress_bar', ''); + } + + /** + We're re-rendering the controls as they are deleted + before heading to next page + Refresh Backgrid to re-render the elements selected + re-render Filter + */ + newModel.trigger("backgrid:refresh", newModel, false); + self.clientSideFilter.render(); + return true; + }, + + beforeNext: function() { return true; }, + + onNext: function(obj){ + + // Assign acls of privModel to main model newModel + if (!_.isUndefined(self.privModel)) { + newModel.set({'acl': self.privModel.get('acl')}); + } + + // Remove the privilege control + if (this.view) { + this.view.cleanup(); + delete this.view; + this.view = null; + } + + // Enable finish button + self.wizard.options.disable_finish = false; + + /** + triggers to get SQL queries data to render + into the reviewSQLPage + */ + newModel.trigger('pgadmin-wizard:nextpage:sql', {'node_type': node_type }); + } + }); + + + ////////////////////////////////////////////////////////////////////// + // // + // Review SQL Query Page // + // // + ////////////////////////////////////////////////////////////////////// + + //Create SqlField Object + var sqlField = new Backform.Field( + { + id: 'sqltab', + label: _('Sql Tab'), + + /** + Extend 'SqlTabControl' to define new + function 'onWizardNextPageChange' + which gets triggered on next button + click to fetch generated SQL query + for the selected db objects. + */ + control: Backform.SqlTabControl.extend({ + initialize: function() { + + // Initialize parent class + Backform.SqlTabControl.prototype.initialize.apply(this, arguments); + + this.msql_url = self.msql_url; + + // define trigger events for prev and next page + this.model.on('pgadmin-wizard:nextpage:sql', this.onWizardNextPageChange, this); + this.model.on('pgadmin-wizard:prevpage:sql', this.onWizardPrevPageChange, this); + }, + + // This method fetches the modified SQL for the wizard + onWizardNextPageChange: function(){ + + var self = this; + + // Fetches modified SQL + $.ajax({ + url: this.msql_url, + type: 'GET', + cache: false, + data: self.model.toJSON(true, 'GET'), + dataType: "json", + contentType: "application/json" + }).done(function(res) { + self.sqlTab.clearHistory(); + self.sqlTab.setValue(res.data); + }).fail(function() { + self.model.trigger('pgadmin-view:msql:error'); + }).always(function() { + self.model.trigger('pgadmin-view:msql:fetched'); + }); + }, + + remove: function() { + + // Clear html dom elements of CodeMirror sql tab + self.sqlControl.unbind(); // Unbind all local event bindings + var cmElem = self.sqlControl.sqlTab.getWrapperElement(); + cmElem.remove(); + self.sqlControl.sqlTab = undefined; + } + + }) + }), + + /** + Create sqlField view instance + to render it into wizard page + */ + sqlControl = self.sqlControl = new (sqlField.get('control'))({ + field: sqlField, + model: newModel + }); + + // Wizard for SQL tab control + var reviewSQLPage = self.reviewSQLPage = new pgBrowser.WizardPage({ + id: 3, + page_title: _('Final (Review Selection) (step 3 of 3)'), + show_description: _('The SQL below will be executed on the ' + + 'database server to grant the selected privileges. ' + + 'Please click on Finish to complete the process. '), + model: newModel, + view: new(function() { + + // Render SqlTab control to generate its html markup + var sqlTabHtml = sqlControl.render().$el; + this.render = function() { + return { el: sqlTabHtml }; + }; + }), + + beforePrev: function(wizardObj) { + + /** + Enable next button if privilege + model is not empty else disable + next button + */ + var aclModel = newModel.get('acl'); + + if(!_.isUndefined(wizardObj.collection) && + wizardObj.collection.models.length > 0) { + if(!_.isUndefined(aclModel) && !_.isEmpty(aclModel) && + aclModel.length > 0) { + wizardObj.collection.at(1).set('disable_next', false); + } + else { + wizardObj.collection.at(1).set('disable_next', true); + } + + return true; + } + }, + }); + + + // Create Wizard Collection of Wizard Pages + var WizardCollection = Backbone.Collection.extend({ + model: pgBrowser.WizardPage + }); + + // It holds all the wizard pages to be rendered + this.wizardCollection = new WizardCollection( + [dbObjectTypePage, privilegePage, reviewSQLPage] + ); + + /** + Create wizard which has following operations: + - renders wizard pages + - defines the first page to render in wizard + - Save the model on finishbutton + - Remove wizard on cancel button + */ + self.wizard = new (pgBrowser.Wizard.extend({ + options: { + title: _('Grant Wizard'), /* Main Wizard Title */ + width: '', + height: '', + curr_page: 0, + show_left_panel: false, + disable_finish: true + }, + + // Callback for finish button + onFinish: function() { + var m = newModel, + d = m.toJSON('GET'); + + // Save model + if (d && !_.isEmpty(d) && !_.isUndefined(d.objects)) { + m.save({}, { + attrs: d, + validate: false, + cache: false, + success: function(res) { + + // Release wizard objects + self.releaseObjects(); + self.close(); + }, + error: function(m, jqxhr) { + alertify.pgNotifier( + "error", jqxhr, + S( + "{{ _('Error during saving properties - %%s!') }}" + ).sprintf(jqxhr.statusText).value() + ); + + // Release wizard objects + self.releaseObjects(); + self.close(); + } + }); + } + }, + + // Callback for cancel button + onCancel: function() { + + // Release wizard objects + self.releaseObjects(); + self.close(); + } + })) ({ + collection: this.wizardCollection, + el: el, + model: newModel + }); + + // Render wizard + self.wizard.render(); + } + }; + }); + } + + // Call Grant Wizard Dialog + alertify.wizardDialog(true); + } + }; + + return pgBrowser.GrantWizard; + }); diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/acl.json b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/acl.json new file mode 100644 index 000000000..2d21f14f3 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/acl.json @@ -0,0 +1,30 @@ +{# List of allowed privileges of Schema, table sequence, function and view #} +{# + Format for allowed privileges are: + "node_name": { + "type": "name", + "acl": [...] + } +#} +{ + "schema": { + "type": "SCHEMA", + "acl": ["a", "r", "w", "d", "D", "x", "t", "U", "X"] + }, + "table": { + "type": "TABLE", + "acl": ["a", "w", "d", "D", "x", "t"] + }, + "view": { + "type": "VIEW", + "acl": ["a", "w", "d", "D", "x", "t"] + }, + "sequence": { + "type": "SEQUENCE", + "acl": ["w", "U"] + }, + "function": { + "type": "FUNCTION", + "acl": ["X"] + } +} diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/function.sql b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/function.sql new file mode 100644 index 000000000..7fad225f8 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/function.sql @@ -0,0 +1,23 @@ +{# ===== Fetch list of Database object types(Functions) ====== #} +{% if type and node_id and nspname %} +{% set func_type = 'Trigger Function' if type == 'trigger_function' else 'Function' %} +SELECT + pr.oid, + pg_get_function_identity_arguments(pr.oid) AS proargs, + {# pr.proname || '(' || pg_get_function_identity_arguments(pr.oid) || ')' AS name,#} + pr.proname AS name, + '{{ nspname }}' AS nspname, + '{{ func_type }}' AS object_type, + '{{ "icon-function" if type != "trigger_function" else "icon-trigger_function" }}' AS icon +FROM + pg_proc pr +JOIN pg_type typ ON typ.oid=prorettype +JOIN pg_namespace typns ON typns.oid=typ.typnamespace +JOIN pg_language lng ON lng.oid=prolang +LEFT OUTER JOIN pg_description des ON (des.objoid=pr.oid AND des.classoid='pg_proc'::regclass) +WHERE + proisagg = FALSE AND pronamespace = {{ node_id }}::oid + AND typname {{ 'NOT' if type != 'trigger_function' else '' }} IN ('trigger', 'event_trigger') +ORDER BY + proname +{% endif %} diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/get_schemas.sql b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/get_schemas.sql new file mode 100644 index 000000000..4f6fd5ae0 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/get_schemas.sql @@ -0,0 +1,19 @@ +{# ===== Fetch list of all schemas ===== #} +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.oid, + nsp.nspname as name +FROM + pg_namespace nsp +WHERE + {% if nspid %} + nsp.oid={{nspid}}::int AND + {% else %} + {% if not show_sysobj %} + nspname NOT LIKE E'pg\_%' AND + {% endif %} + {% endif %} + NOT ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY nspname; diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/grant_function.sql b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/grant_function.sql new file mode 100644 index 000000000..aea68af31 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/grant_function.sql @@ -0,0 +1,10 @@ +{# ===== Grant Permissions on Database Objects Selected ==== #} +{% import 'macros/functions/privilege.macros' as PRIVILEGE_FUNCTION %} +{% for obj in data.objects -%} +{% for priv in data.priv -%} +{# ===== if object_type is Function then apply function marcros ===== #} +{% if (obj.object_type == 'Function' or obj.object_type == 'Trigger Function') %} +{{ PRIVILEGE_FUNCTION.SET(conn, 'FUNCTION', priv['grantee'], obj.name, priv['without_grant'], priv['with_grant'], obj.nspname, obj.proargs)}} +{% endif -%} +{% endfor -%} +{% endfor -%} diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/grant_sequence.sql b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/grant_sequence.sql new file mode 100644 index 000000000..8e25d1788 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/grant_sequence.sql @@ -0,0 +1,9 @@ +{# ===== Grant Permissions on Database Objects Selected ==== #} +{% import 'macros/schemas/privilege.macros' as PRIVILEGE %} +{% for obj in data.objects -%} +{% for priv in data.priv -%} +{% if obj.object_type == 'Sequence' %} +{{ PRIVILEGE.SET(conn, 'SEQUENCE', priv['grantee'], obj.name, priv['without_grant'], priv['with_grant'], obj.nspname ) }} +{% endif %} +{% endfor -%} +{% endfor -%} diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/grant_table.sql b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/grant_table.sql new file mode 100644 index 000000000..c74b86ef0 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/grant_table.sql @@ -0,0 +1,9 @@ +{# ===== Grant Permissions on Database Objects Selected ==== #} +{% import 'macros/schemas/privilege.macros' as PRIVILEGE %} +{% for obj in data.objects -%} +{% for priv in data.priv -%} +{% if obj.object_type == 'Table' or obj.object_type == 'View' %} +{{ PRIVILEGE.SET(conn, 'TABLE', priv['grantee'], obj.name, priv['without_grant'], priv['with_grant'], obj.nspname ) }} +{% endif %} +{% endfor -%} +{% endfor -%} diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/sequence.sql b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/sequence.sql new file mode 100644 index 000000000..2950df145 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/sequence.sql @@ -0,0 +1,15 @@ +{# ===== Fetch list of Database object types(Sequence) ===== #} +{% if node_id, nspname %} +SELECT + cl.relname AS name, + 'Sequence' AS object_type, + 'icon-sequence' AS icon, + '{{ nspname }}' AS nspname +FROM + pg_class cl +LEFT OUTER JOIN pg_description des ON (des.objoid=cl.oid AND des.classoid='pg_class'::regclass) +WHERE + relkind = 'S' AND relnamespace = {{ node_id }}::oid +ORDER BY + cl.relname +{% endif %} diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/table.sql b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/table.sql new file mode 100644 index 000000000..5f67c4965 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/table.sql @@ -0,0 +1,19 @@ +{# ===== Fetch list of Database object types(Tables) ===== #} +{% if node_id and nspname %} +SELECT + rel.relname AS name, + 'Table' AS object_type, + 'icon-table' AS icon, + '{{ nspname }}' AS nspname +FROM + pg_class rel +LEFT OUTER JOIN pg_tablespace spc ON spc.oid=rel.reltablespace +LEFT OUTER JOIN pg_description des ON (des.objoid=rel.oid AND des.objsubid=0 AND des.classoid='pg_class'::regclass) +LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p' +LEFT OUTER JOIN pg_class tst ON tst.oid = rel.reltoastrelid +LEFT JOIN pg_type typ ON rel.reloftype=typ.oid +WHERE + rel.relkind IN ('r','s','t') AND rel.relnamespace = {{ node_id }}::oid +ORDER BY + rel.relname +{% endif %} diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/view.sql b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/view.sql new file mode 100644 index 000000000..fab77cc70 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/pg/9.1_plus/sql/view.sql @@ -0,0 +1,28 @@ +{# ===== Fetch list of Database object types(View) ===== #} +{% if node_id %} +SELECT + c.relname AS name, + 'View' AS object_type, + 'icon-view' AS icon, + '{{ nspname }}' AS nspname +FROM + pg_class c +LEFT OUTER JOIN pg_tablespace spc ON spc.oid=c.reltablespace +LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass) +LEFT OUTER JOIN pg_class tst ON tst.oid = c.reltoastrelid +WHERE + ((c.relhasrules AND (EXISTS ( + SELECT + r.rulename + FROM + pg_rewrite r + WHERE + ((r.ev_class = c.oid) + AND (bpchar(r.ev_type) = '1'::bpchar)) + )) + ) OR (c.relkind = 'v'::char) + ) + AND c.relnamespace = {{ node_id }}::oid +ORDER BY + c.relname +{% endif %} diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/acl.json b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/acl.json new file mode 100644 index 000000000..2d21f14f3 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/acl.json @@ -0,0 +1,30 @@ +{# List of allowed privileges of Schema, table sequence, function and view #} +{# + Format for allowed privileges are: + "node_name": { + "type": "name", + "acl": [...] + } +#} +{ + "schema": { + "type": "SCHEMA", + "acl": ["a", "r", "w", "d", "D", "x", "t", "U", "X"] + }, + "table": { + "type": "TABLE", + "acl": ["a", "w", "d", "D", "x", "t"] + }, + "view": { + "type": "VIEW", + "acl": ["a", "w", "d", "D", "x", "t"] + }, + "sequence": { + "type": "SEQUENCE", + "acl": ["w", "U"] + }, + "function": { + "type": "FUNCTION", + "acl": ["X"] + } +} diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/function.sql b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/function.sql new file mode 100644 index 000000000..7fad225f8 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/function.sql @@ -0,0 +1,23 @@ +{# ===== Fetch list of Database object types(Functions) ====== #} +{% if type and node_id and nspname %} +{% set func_type = 'Trigger Function' if type == 'trigger_function' else 'Function' %} +SELECT + pr.oid, + pg_get_function_identity_arguments(pr.oid) AS proargs, + {# pr.proname || '(' || pg_get_function_identity_arguments(pr.oid) || ')' AS name,#} + pr.proname AS name, + '{{ nspname }}' AS nspname, + '{{ func_type }}' AS object_type, + '{{ "icon-function" if type != "trigger_function" else "icon-trigger_function" }}' AS icon +FROM + pg_proc pr +JOIN pg_type typ ON typ.oid=prorettype +JOIN pg_namespace typns ON typns.oid=typ.typnamespace +JOIN pg_language lng ON lng.oid=prolang +LEFT OUTER JOIN pg_description des ON (des.objoid=pr.oid AND des.classoid='pg_proc'::regclass) +WHERE + proisagg = FALSE AND pronamespace = {{ node_id }}::oid + AND typname {{ 'NOT' if type != 'trigger_function' else '' }} IN ('trigger', 'event_trigger') +ORDER BY + proname +{% endif %} diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/get_schemas.sql b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/get_schemas.sql new file mode 100644 index 000000000..4f6fd5ae0 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/get_schemas.sql @@ -0,0 +1,19 @@ +{# ===== Fetch list of all schemas ===== #} +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.oid, + nsp.nspname as name +FROM + pg_namespace nsp +WHERE + {% if nspid %} + nsp.oid={{nspid}}::int AND + {% else %} + {% if not show_sysobj %} + nspname NOT LIKE E'pg\_%' AND + {% endif %} + {% endif %} + NOT ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY nspname; diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/grant_function.sql b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/grant_function.sql new file mode 100644 index 000000000..aea68af31 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/grant_function.sql @@ -0,0 +1,10 @@ +{# ===== Grant Permissions on Database Objects Selected ==== #} +{% import 'macros/functions/privilege.macros' as PRIVILEGE_FUNCTION %} +{% for obj in data.objects -%} +{% for priv in data.priv -%} +{# ===== if object_type is Function then apply function marcros ===== #} +{% if (obj.object_type == 'Function' or obj.object_type == 'Trigger Function') %} +{{ PRIVILEGE_FUNCTION.SET(conn, 'FUNCTION', priv['grantee'], obj.name, priv['without_grant'], priv['with_grant'], obj.nspname, obj.proargs)}} +{% endif -%} +{% endfor -%} +{% endfor -%} diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/grant_sequence.sql b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/grant_sequence.sql new file mode 100644 index 000000000..8e25d1788 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/grant_sequence.sql @@ -0,0 +1,9 @@ +{# ===== Grant Permissions on Database Objects Selected ==== #} +{% import 'macros/schemas/privilege.macros' as PRIVILEGE %} +{% for obj in data.objects -%} +{% for priv in data.priv -%} +{% if obj.object_type == 'Sequence' %} +{{ PRIVILEGE.SET(conn, 'SEQUENCE', priv['grantee'], obj.name, priv['without_grant'], priv['with_grant'], obj.nspname ) }} +{% endif %} +{% endfor -%} +{% endfor -%} diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/grant_table.sql b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/grant_table.sql new file mode 100644 index 000000000..c74b86ef0 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/grant_table.sql @@ -0,0 +1,9 @@ +{# ===== Grant Permissions on Database Objects Selected ==== #} +{% import 'macros/schemas/privilege.macros' as PRIVILEGE %} +{% for obj in data.objects -%} +{% for priv in data.priv -%} +{% if obj.object_type == 'Table' or obj.object_type == 'View' %} +{{ PRIVILEGE.SET(conn, 'TABLE', priv['grantee'], obj.name, priv['without_grant'], priv['with_grant'], obj.nspname ) }} +{% endif %} +{% endfor -%} +{% endfor -%} diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/sequence.sql b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/sequence.sql new file mode 100644 index 000000000..2950df145 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/sequence.sql @@ -0,0 +1,15 @@ +{# ===== Fetch list of Database object types(Sequence) ===== #} +{% if node_id, nspname %} +SELECT + cl.relname AS name, + 'Sequence' AS object_type, + 'icon-sequence' AS icon, + '{{ nspname }}' AS nspname +FROM + pg_class cl +LEFT OUTER JOIN pg_description des ON (des.objoid=cl.oid AND des.classoid='pg_class'::regclass) +WHERE + relkind = 'S' AND relnamespace = {{ node_id }}::oid +ORDER BY + cl.relname +{% endif %} diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/table.sql b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/table.sql new file mode 100644 index 000000000..5f67c4965 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/table.sql @@ -0,0 +1,19 @@ +{# ===== Fetch list of Database object types(Tables) ===== #} +{% if node_id and nspname %} +SELECT + rel.relname AS name, + 'Table' AS object_type, + 'icon-table' AS icon, + '{{ nspname }}' AS nspname +FROM + pg_class rel +LEFT OUTER JOIN pg_tablespace spc ON spc.oid=rel.reltablespace +LEFT OUTER JOIN pg_description des ON (des.objoid=rel.oid AND des.objsubid=0 AND des.classoid='pg_class'::regclass) +LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid AND con.contype='p' +LEFT OUTER JOIN pg_class tst ON tst.oid = rel.reltoastrelid +LEFT JOIN pg_type typ ON rel.reloftype=typ.oid +WHERE + rel.relkind IN ('r','s','t') AND rel.relnamespace = {{ node_id }}::oid +ORDER BY + rel.relname +{% endif %} diff --git a/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/view.sql b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/view.sql new file mode 100644 index 000000000..fab77cc70 --- /dev/null +++ b/web/pgadmin/tools/grant_wizard/templates/grant_wizard/ppas/9.1_plus/sql/view.sql @@ -0,0 +1,28 @@ +{# ===== Fetch list of Database object types(View) ===== #} +{% if node_id %} +SELECT + c.relname AS name, + 'View' AS object_type, + 'icon-view' AS icon, + '{{ nspname }}' AS nspname +FROM + pg_class c +LEFT OUTER JOIN pg_tablespace spc ON spc.oid=c.reltablespace +LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass) +LEFT OUTER JOIN pg_class tst ON tst.oid = c.reltoastrelid +WHERE + ((c.relhasrules AND (EXISTS ( + SELECT + r.rulename + FROM + pg_rewrite r + WHERE + ((r.ev_class = c.oid) + AND (bpchar(r.ev_type) = '1'::bpchar)) + )) + ) OR (c.relkind = 'v'::char) + ) + AND c.relnamespace = {{ node_id }}::oid +ORDER BY + c.relname +{% endif %}