30-07-2020 10:40 AM - last edited on 15-11-2021 10:58 PM by exa-MathiasHo
Users can see more senior or different roles than they have been granted.
You can see this plainly in the demo accounts. Is this is a feature or a bug?
Solved! Go to Solution.
05-08-2020 6:05 AM
Nico came up with a great solution and is either too modest or too busy to post to the forum. I sleep better tonight.
He created a preprocessor which searches for the tables EXA_ALL_ROLES and EXA_ALL_TABLES within the SQL text sent by the user, and if it detects these strings, then it will check if the user has the SELECT ANY DICTIONARY privilege (which lets users see every statistics/sys table and is also granted to DBA) as a role.
When a user who doesn't have SELECT ANY DICTIONARY runs SELECT * FROM EXA_ALL_ROLES, they get this error message which you can customize.
[Code: 0, SQL State: 43100] While preprocessing SQL with "ALLSCRIPTS"."PP_HIDE_TABLES": 43000:"You must be a DBA or have SELECT ANY DICTIONARY to run this query" caught in script "ALLSCRIPTS"."PP_HIDE_TABLES" at line 18 (Session: 1673753318205978533)
This is an extremely simple script and would need to be expanded for other cases/tables/roles, etc)
--/
CREATE OR REPLACE LUA SCRIPT "ALLSCRIPTS"."PP_HIDE_TABLES" () RETURNS ROWCOUNT AS
sql_text = sqlparsing.getsqltext()
sql_text = string.upper(sql_text)
tokens = sqlparsing.tokenize(sql_text)
disallowed_table_names = {'EXA_ALL_ROLES','EXA_ALL_USERS'}
for i=1, #disallowed_table_names do
matched_name = sqlparsing.find(tokens,1,true,true,sqlparsing.iswhitespaceorcomment,disallowed_table_names[i])
if matched_name ~= nil then
string_found = true
for i=1, #matched_name do
--if a match is found, check if they have SELECT ANY DICTIONARY
if sqlparsing.isidentifier(tokens[matched_name[i]]) then
res = query([[SELECT * FROM EXA_ROLE_SYS_PRIVS WHERE PRIVILEGE = 'SELECT ANY DICTIONARY' UNION ALL SELECT * FROM EXA_USER_SYS_PRIVS WHERE PRIVILEGE = 'SELECT ANY DICTIONARY';]])
if #res == 0 then
-- User does not have SELECT ANY DICTIONARY privilege - throws error
output([[error]])
error([[You must be a DBA or have SELECT ANY DICTIONARY to run this query]])
end
else
output([[No identifier]])
--sqlparsing.setsqltext(sql_text)
end
end
end
end
output(sql_text)
sqlparsing.setsqltext(sql_text)
/
alter system set sql_preprocessor_script = "ALLSCRIPTS"."PP_HIDE_TABLES";
exa-Nico! exa-Nico! exa-Nico! exa-Nico! exa-Nico! exa-Nico!
30-07-2020 12:17 PM
Hi drumcircle,
this is intentionally so.
Every user can see all existing roles with EXA_ALL_ROLES - not only through this particular SQL Client your screenshot comes from.
Of course that doesn't mean every user can exercise any priviliges contained in these roles.
Best regards
Uwe
30-07-2020 3:03 PM
Hi, in what Scenario would this create an issue? Rgds Christian
31-07-2020 5:53 AM - edited 31-07-2020 5:54 AM
One of my clients grants privileges for multi-tenancy by role, so schema permissions need not be assigned to every single user. They actually decided not to use roles at all due to this visibility leakage issue. Is there another approach to multi-tenant security that makes this a non-issue?
31-07-2020 8:31 AM
Could ALL_ROLES visibility be made configurable???
31-07-2020 9:41 AM
The visibility of EXA_ALL_ROLES is not configurable. To change it would be an entire new improvement request/feature, but to be honest, I'm not sure if it makes sense in this case. Simply knowing the role name and that the role exists does not necessarily make it dangerous. You could make the role names so convoluted that knowing the role name won't give any information as to it's purpose or what access it's granting.
I would not recommend doing multi-tenancy on users. For one, if you accidentally delete a user who is the owner of a schema, then the schema data is completely gone. Roles protect you from that. It's also much easier to manage permissions if all you have to do is add or remove roles to/from a user. Also, users are also able to see the names of every other user in the database via EXA_ALL_USERS.
One improvement in version 7.0 would be that we are introducing a new USAGE privilege which will be able to hide all schema information (for example in EXA_SCHEMAS) or in your database browser, so unless you have this privilege, you don't know that the role is the owner of a schema that you are not allowed to see.
Another option for multi-tenancy would be to create completely separate databases for each tenant, but this is significantly more expensive 😁
Not sure if you've seen this article, but maybe it helps a bit!
01-08-2020 6:15 AM
03-08-2020 9:50 AM
05-08-2020 6:05 AM
Nico came up with a great solution and is either too modest or too busy to post to the forum. I sleep better tonight.
He created a preprocessor which searches for the tables EXA_ALL_ROLES and EXA_ALL_TABLES within the SQL text sent by the user, and if it detects these strings, then it will check if the user has the SELECT ANY DICTIONARY privilege (which lets users see every statistics/sys table and is also granted to DBA) as a role.
When a user who doesn't have SELECT ANY DICTIONARY runs SELECT * FROM EXA_ALL_ROLES, they get this error message which you can customize.
[Code: 0, SQL State: 43100] While preprocessing SQL with "ALLSCRIPTS"."PP_HIDE_TABLES": 43000:"You must be a DBA or have SELECT ANY DICTIONARY to run this query" caught in script "ALLSCRIPTS"."PP_HIDE_TABLES" at line 18 (Session: 1673753318205978533)
This is an extremely simple script and would need to be expanded for other cases/tables/roles, etc)
--/
CREATE OR REPLACE LUA SCRIPT "ALLSCRIPTS"."PP_HIDE_TABLES" () RETURNS ROWCOUNT AS
sql_text = sqlparsing.getsqltext()
sql_text = string.upper(sql_text)
tokens = sqlparsing.tokenize(sql_text)
disallowed_table_names = {'EXA_ALL_ROLES','EXA_ALL_USERS'}
for i=1, #disallowed_table_names do
matched_name = sqlparsing.find(tokens,1,true,true,sqlparsing.iswhitespaceorcomment,disallowed_table_names[i])
if matched_name ~= nil then
string_found = true
for i=1, #matched_name do
--if a match is found, check if they have SELECT ANY DICTIONARY
if sqlparsing.isidentifier(tokens[matched_name[i]]) then
res = query([[SELECT * FROM EXA_ROLE_SYS_PRIVS WHERE PRIVILEGE = 'SELECT ANY DICTIONARY' UNION ALL SELECT * FROM EXA_USER_SYS_PRIVS WHERE PRIVILEGE = 'SELECT ANY DICTIONARY';]])
if #res == 0 then
-- User does not have SELECT ANY DICTIONARY privilege - throws error
output([[error]])
error([[You must be a DBA or have SELECT ANY DICTIONARY to run this query]])
end
else
output([[No identifier]])
--sqlparsing.setsqltext(sql_text)
end
end
end
end
output(sql_text)
sqlparsing.setsqltext(sql_text)
/
alter system set sql_preprocessor_script = "ALLSCRIPTS"."PP_HIDE_TABLES";
exa-Nico! exa-Nico! exa-Nico! exa-Nico! exa-Nico! exa-Nico!
14-02-2022 11:03 PM - edited 14-02-2022 11:07 PM
You're kidding, right? I've never met a DBA who would want every user to know the logins of every other user or the capabilities exposed via roles. In an OEM/extranet situation, this is undesirable as shown by the need for obfuscated user names in the Exasol demo system.
Then it's time to become part of a unique family! Discover helpful tips and support other Community members with your knowledge.
Sign In\n\t\t\t\t\t\tSorry, unable to complete the action you requested.\n\t\t\t\t\t<\/p>\n\t\t\t\t\n\n\t\t\t\t\n\n\t\t\t\t\n\n\t\t\t\t\n\t\t\t<\/div>\n\n\t\t\t\n\t\t<\/div>"; LITHIUM.AjaxSupport({"ajaxOptionsParam":{"event":"LITHIUM:lightboxRenderComponent","parameters":{"componentParams":"{\n \"triggerSelector\" : {\n \"value\" : \"#loginPageV2_32a03c5e7bae20\",\n \"class\" : \"lithium.util.css.CssSelector\"\n }\n}","componentId":"authentication.widget.login-dialog-content"},"trackableEvent":true},"tokenId":"ajax","elementSelector":"#loginPageV2_32a03c5e7bae20","action":"lightboxRenderComponent","feedbackSelector":false,"url":"https://community.exasol.com/t5/forums/v5/forumtopicpage.loginpagev2:lightboxrendercomponent?t:ac=board-id/GeneralDiscussions/message-id/1971&t:cp=authentication/contributions/actions","ajaxErrorEventName":"LITHIUM:ajaxError","token":"KWfYAeFK3QfvYGbvfIlgz9aRSvej5QUg8774CZfGWlw."}); LITHIUM.Dialog.options['-622891615'] = {"contentContext":"authentication.widget.login-dialog-content","dialogOptions":{"trackable":true,"resizable":true,"autoOpen":false,"minWidth":710,"dialogClass":"lia-content lia-panel-dialog lia-panel-dialog-modeless-advanced","title":"","minHeight":200,"fitInWindow":true,"draggable":true,"maxHeight":600,"width":710,"position":["center","center"],"modal":false,"maxWidth":710},"contentType":"ajax"}; LITHIUM.Dialog({ "closeImageIconURL" : "https://community.exasol.com/skins/images/846A2A0EB0D4BBA1CCA2564AC77177B7/responsive_peak/images/button_dialog_close.svg", "closeEvent" : "LITHIUM:lightboxCloseEvent", "activecastFullscreen" : false, "defaultAriaLabel" : "", "clientId" : "loginPageV2_32a03c5e7bae20", "accessibility" : false, "buttonDialogCloseAlt" : "Close", "triggerSelector" : ".lia-panel-dialog-trigger-event-click", "ajaxEvent" : "LITHIUM:lightboxRenderComponent", "dialogContentCssClass" : "lia-panel-dialog-content", "triggerEvent" : "click", "dialogKey" : "dialogKey" }); LITHIUM.Form.resetFieldForFocusFound(); LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox","feedbackSelector":".InfoMessage"}); LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_0","feedbackSelector":".InfoMessage"}); LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_1","feedbackSelector":".InfoMessage"}); LITHIUM.InformationBox({"updateFeedbackEvent":"LITHIUM:updateAjaxFeedback","componentSelector":"#informationbox_2","feedbackSelector":".InfoMessage"}); LITHIUM.AjaxFeedback(".lia-inline-ajax-feedback", "LITHIUM:hideAjaxFeedback", ".lia-inline-ajax-feedback-persist"); LITHIUM.Placeholder(); LITHIUM.AutoComplete({"options":{"triggerTextLength":0,"updateInputOnSelect":true,"loadingText":"Searching...","emptyText":"No Matches","successText":"Results:","defaultText":"Enter a search word","disabled":false,"footerContent":[{"scripts":"\n\n;(function($){LITHIUM.Link=function(params){var $doc=$(document);function handler(event){var $link=$(this);var token=$link.data('lia-action-token');if($link.data('lia-ajax')!==true&&token!==undefined){if(event.isPropagationStopped()===false&&event.isImmediatePropagationStopped()===false&&event.isDefaultPrevented()===false){event.stop();var $form=$('