diff --git a/core/doctype/defaultvalue/defaultvalue.py b/core/doctype/defaultvalue/defaultvalue.py
index b17817bed9..3701a2ecb9 100644
--- a/core/doctype/defaultvalue/defaultvalue.py
+++ b/core/doctype/defaultvalue/defaultvalue.py
@@ -13,4 +13,10 @@ def on_doctype_update():
where Key_name="defaultvalue_parent_defkey_index" """):
webnotes.conn.commit()
webnotes.conn.sql("""alter table `tabDefaultValue`
- add index defaultvalue_parent_defkey_index(parent, defkey)""")
\ No newline at end of file
+ add index defaultvalue_parent_defkey_index(parent, defkey)""")
+
+ if not webnotes.conn.sql("""show index from `tabDefaultValue`
+ where Key_name="defaultvalue_parent_parenttype_index" """):
+ webnotes.conn.commit()
+ webnotes.conn.sql("""alter table `tabDefaultValue`
+ add index defaultvalue_parent_parenttype_index(parent, parentttype)""")
\ No newline at end of file
diff --git a/core/doctype/docfield/docfield.txt b/core/doctype/docfield/docfield.txt
index 7362a518d7..3b0a612653 100644
--- a/core/doctype/docfield/docfield.txt
+++ b/core/doctype/docfield/docfield.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-02-22 01:27:33",
"docstatus": 0,
- "modified": "2013-07-10 14:54:08",
+ "modified": "2013-11-26 16:34:15",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -132,6 +132,13 @@
"print_width": "50px",
"width": "50px"
},
+ {
+ "description": "User restrictions should not apply for this Link",
+ "doctype": "DocField",
+ "fieldname": "ignore_restriction",
+ "fieldtype": "Check",
+ "label": "Ignore Restriction"
+ },
{
"doctype": "DocField",
"fieldname": "width",
diff --git a/core/page/permission_manager/permission_manager.js b/core/page/permission_manager/permission_manager.js
index c519680ead..4f8488bb38 100644
--- a/core/page/permission_manager/permission_manager.js
+++ b/core/page/permission_manager/permission_manager.js
@@ -41,27 +41,12 @@ wn.pages['permission-manager'].onload = function(wrapper) {
"+wn._("Restricting By User")+": \
\
"+wn._("To restrict a User of a particular Role to documents that are only self-created.")+
- wn._("Click on button in the 'Condition' column and select the option 'User is the creator of the document'")+". \
- "+wn._("To restrict a User of a particular Role to documents that are explicitly assigned to them")+ ":"+
- + wn._("create a Custom Field of type Link (Profile) and then use the 'Condition' settings to map that field to the Permission rule.")+
- " \
+ wn._("Click on button in the 'Condition' column and select the option 'User is the creator of the document'")+".\
\
\
"+wn._("Advanced Settings")+": \
- "+wn._("To further restrict permissions based on certain values in a document, use the 'Condition' settings.")+" "+
- wn._("For example: You want to restrict users to transactions marked with a certain property called 'Territory'")+":
\
- \
- "+wn._("Make sure that the transactions you want to restrict have a Link field 'territory' that maps to a 'Territory' master.")+" "
- +wn._("If not, create a")+
- ""+wn._("Custom Field")+" "+ wn._("of type Link")+". \
- "+wn._("In the Permission Manager, click on the button in the 'Condition' column for the Role you want to restrict.")+" \
- "+wn._("A new popup will open that will ask you to select further conditions.")+
- wn._("If the 'territory' Link Field exists, it will give you an option to select it")+". \
- "+wn._("Go to Setup > User Properties to set \
- 'territory' for diffent Users.")+" \
- \
- "+wn._("Once you have set this, the users will only be able access documents with that property.")+"
\
- \
+ "+wn._("To further restrict permissions based on certain values, like Company or Territory in a document, please go to User Restrictions ")+" "+
+ "
"+wn._("Once you have set this, the users will only be able access documents where the link (e.g Company) exists.")+"
\
"+wn._("If these instructions where not helpful, please add in your suggestions at GitHub Issues ")+"
\
\
");
@@ -390,23 +375,6 @@ wn.PermissionEngine = Class.extend({
The user is the creator of the document.\
").css("padding", "15px");
- // profile fields
- $.each(me.get_profile_fields(perm.parent), function(i, d) {
- $("\
- Value of field "+d.label+" is the User.\
- ").appendTo(dialog.body);
- });
-
- // add options for all link fields
- $.each(me.get_link_fields(perm.parent), function(i, d) {
- $("\
- "+d.label+" in "+d.parent+" matches User Property "
- +d.fieldname+" .\
- ").appendTo(dialog.body);
- });
-
// button
$("Update ")
.appendTo($("").appendTo(dialog.body))
diff --git a/core/page/user_properties/user_properties.js b/core/page/user_properties/user_properties.js
index 08d94a8626..30946e5220 100644
--- a/core/page/user_properties/user_properties.js
+++ b/core/page/user_properties/user_properties.js
@@ -1,19 +1,19 @@
wn.pages['user-properties'].onload = function(wrapper) {
wn.ui.make_app_page({
parent: wrapper,
- title: 'User Properties',
+ title: 'User Permission Restrictions',
single_column: true
});
- $(wrapper).find(".layout-main").html("
\
+ $(wrapper).find(".layout-main").html("
\
\
\
- "+wn._("Quick Help for User Properties")+": \
+ "+wn._("Quick Help for Permission Restrictions")+": \
\
- "+wn._("You can set various 'properties' to Users to set default values and apply permission rules based on the value of these properties in various forms.")+" \
- "+wn._("These properties are Link Type fields from all Documents.")+" \
- "+wn._("These properties will appear as values in forms that contain them.")+" \
- "+wn._("These properties can also be used to 'assign' a particular document, whose property matches with the User's property to a User. These can be set using the Permission Manager ")+" \
- "+wn._("A user can have multiple values for a property.")+" \
+ "+wn._("Apart from the existing Permission Rules, you can apply addition restriction based on Type.")+" \
+ "+wn._("These restrictions will apply for all transactions linked to the restricted record.")
+ +wn._("For example, if user X is restricted to company C, user X will not be able to see any transaction that has company C as a linked value.")+" \
+ "+wn._("These will also be set as default values for those links.")+" \
+ "+wn._("A user can be restricted to multiple records of the same type.")+" \
\
\
");
@@ -42,14 +42,12 @@ wn.UserProperties = Class.extend({
me.user_select =
me.wrapper.appframe.add_select("users",
["Select User..."].concat(r.message.users))
- .css("width", "200px")
.change(function() {
me.set_route();
});
me.property_select =
me.wrapper.appframe.add_select("links",
["Select Property..."].concat(me.get_link_names()))
- .css("width", "200px")
.change(function() {
me.set_route();
});
@@ -121,7 +119,7 @@ wn.UserProperties = Class.extend({
\
").appendTo(this.body);
- $.each([[wn._("User"), 150], [wn._("Property"), 150], [wn._("Value"),150], ["", 50]],
+ $.each([[wn._("User"), 150], [wn._("Type"), 150], [wn._("Restricted To"),150], ["", 50]],
function(i, col) {
$("").html(col[0]).css("width", col[1]+"px")
.appendTo(me.table.find("thead tr"));
@@ -168,7 +166,7 @@ wn.UserProperties = Class.extend({
show_add_property: function() {
var me = this;
- $(""+wn._("Add A Property")+" ")
+ $(""+wn._("Add A Restriction")+" ")
.appendTo($("").appendTo(this.body))
.click(function() {
var d = new wn.ui.Dialog({
@@ -229,6 +227,5 @@ wn.UserProperties = Class.extend({
});
d.show();
});
-
}
})
diff --git a/core/page/user_properties/user_properties.py b/core/page/user_properties/user_properties.py
index 50a80384da..0d0c268b06 100644
--- a/core/page/user_properties/user_properties.py
+++ b/core/page/user_properties/user_properties.py
@@ -8,28 +8,12 @@ import webnotes.defaults
@webnotes.whitelist()
def get_users_and_links():
webnotes.only_for(("System Manager", "Administrator"))
- links, all_fields = [], []
-
- for l in webnotes.conn.sql("""select tabDocField.fieldname, tabDocField.options
- from tabDocField, tabDocType
- where tabDocField.fieldtype='Link'
- and tabDocField.parent = tabDocType.name
- and ifnull(tabDocType.istable,0)=0
- and ifnull(tabDocType.issingle,0)=0
- and tabDocField.parent not in ('[Select]', 'DocType', 'Module Def')
- """) + webnotes.conn.sql("""select fieldname, options
- from `tabCustom Field` where fieldtype='Link'"""):
- if not l[0] in all_fields:
- links.append([l[0], l[1]])
- all_fields.append(l[0])
-
- links.sort()
-
return {
"users": [d[0] for d in webnotes.conn.sql("""select name from tabProfile where
ifnull(enabled,0)=1 and
name not in ("Administrator", "Guest")""")],
- "link_fields": links
+ "link_fields": webnotes.conn.sql("""select name, name from tabDocType
+ where ifnull(issingle,0)=0 and ifnull(istable,0)=0""")
}
@webnotes.whitelist()
@@ -38,6 +22,7 @@ def get_properties(user=None, key=None):
return webnotes.conn.sql("""select name, parent, defkey, defvalue
from tabDefaultValue
where parent!='Control Panel'
+ and parenttype='Restriction'
and substr(defkey,1,1)!='_'
%s%s order by parent, defkey""" % (\
user and (" and parent='%s'" % user) or "",
@@ -51,8 +36,4 @@ def remove(user, name):
@webnotes.whitelist()
def add(parent, defkey, defvalue):
webnotes.only_for(("System Manager", "Administrator"))
- webnotes.defaults.add_user_default(defkey, defvalue, parent)
-
-def get_defvalue(doctype, txt, searchfield, start, page_len, filters):
- return webnotes.conn.sql("""select name from `tab%s` where name like %s limit 20""" %
- (filters.get("doctype"), "%s"), "%s%%" % (txt,))
\ No newline at end of file
+ webnotes.add_default(defkey, defvalue, parent, "Restriction")
\ No newline at end of file
diff --git a/public/js/wn/form/control.js b/public/js/wn/form/control.js
index 462062d922..26e7abdc17 100644
--- a/public/js/wn/form/control.js
+++ b/public/js/wn/form/control.js
@@ -104,18 +104,19 @@ wn.ui.form.ControlImage = wn.ui.form.Control.extend({
this._super();
var me = this;
this.$wrapper
- .css({"margin-bottom": "10px", "margin-right": "15px", "float": "right", "text-align": "right", "max-width": "100%"})
- .on("refresh", function() {
- me.$wrapper.empty();
- if(me.df.options && me.frm.doc[me.df.options]) {
- $(" ")
- .appendTo(me.$wrapper);
- } else {
- $("
")
- .appendTo(me.$wrapper)
- }
- return false;
- })
+ .css({"margin-bottom": "10px", "margin-right": "15px", "float": "right",
+ "text-align": "right", "max-width": "100%"})
+ .on("refresh", function() {
+ me.$wrapper.empty();
+ if(me.df.options && me.frm.doc[me.df.options]) {
+ $(" ")
+ .appendTo(me.$wrapper);
+ } else {
+ $("
")
+ .appendTo(me.$wrapper)
+ }
+ return false;
+ });
}
});
diff --git a/public/js/wn/model/create_new.js b/public/js/wn/model/create_new.js
index 86f31c2c7e..3808174e9e 100644
--- a/public/js/wn/model/create_new.js
+++ b/public/js/wn/model/create_new.js
@@ -64,7 +64,12 @@ $.extend(wn.model, {
"Today": dateutil.get_today(),
}
- if(wn.defaults.get_user_default(df.fieldname))
+ if(df.fieldtype==="Link" && wn.boot.restrictions
+ && df.ignore_restrictions != 1
+ && wn.boot.restrictions[df.options]
+ && (wn.boot.restrictions[df.options].length===1))
+ return wn.boot.restrictions[df.options][0];
+ else if(wn.defaults.get_user_default(df.fieldname))
return wn.defaults.get_user_default(df.fieldname);
else if(df["default"] && df["default"][0]===":")
return wn.model.get_default_from_boot_docs(df, doc, parent_doc);
diff --git a/webnotes/__init__.py b/webnotes/__init__.py
index 677e9be363..eec36d3186 100644
--- a/webnotes/__init__.py
+++ b/webnotes/__init__.py
@@ -71,6 +71,7 @@ _response = local("_response")
session = local("session")
user = local("user")
flags = local("flags")
+restrictions = local("restrictions")
error_log = local("error_log")
debug_log = local("debug_log")
@@ -93,6 +94,7 @@ def init(site=None):
local.initialised = True
local.flags = _dict({})
local.rollback_observers = []
+ local.restrictions = None
def destroy():
"""closes connection and releases werkzeug local"""
@@ -347,36 +349,28 @@ def has_permission(doctype, ptype="read", refdoc=None):
return perms and True or False
def has_match(meta, perms, refdoc):
- from webnotes.defaults import get_user_default_as_list
+ from webnotes.defaults import get_restrictions
+
+ restrictions = get_restrictions()
+ if not restrictions:
+ return True
if isinstance(refdoc, basestring):
refdoc = doc(meta[0].name, refdoc)
- match_failed = {}
- for p in perms:
- if p.match:
- if ":" in p.match:
- keys = p.match.split(":")
- else:
- keys = [p.match, p.match]
-
- if refdoc.fields.get(keys[0],"[No Value]") in get_user_default_as_list(keys[1]):
- return True
- else:
- match_failed[keys[0]] = refdoc.fields.get(keys[0],"[No Value]")
- else:
- # found a permission without a match
- return True
-
- # no valid permission found
- if match_failed:
- msg = _("Not allowed for: ")
- for key in match_failed:
- msg += "\n" + (meta.get_field(key) and meta.get_label(key) or key) \
- + " = " + (match_failed[key] or "None")
- msgprint(msg)
+ fields_to_check = meta.get({"DocType":"DocField", "parent":meta[0].name, "fieldtype":"Link",
+ "options":("in", restrictions.keys()), "ignore_restrictions":("!=", 1)})
- return False
+ if meta[0].name in restrictions:
+ fields_to_check.append(_dict({"label":"Name", "fieldname":"name"}))
+
+ for df in fields_to_check:
+ if refdoc.get(df.fieldname) not in restrictions[meta[0].name]:
+ msg = _("Not allowed for: ") + df.label + " equals " + refdoc.get(df.fieldname)
+ msgprint(msg)
+ return False
+
+ return True
def generate_hash():
"""Generates random hash for session id"""
diff --git a/webnotes/boot.py b/webnotes/boot.py
index 2bb53276f9..826b68bc3d 100644
--- a/webnotes/boot.py
+++ b/webnotes/boot.py
@@ -27,6 +27,7 @@ def get_bootinfo():
# system info
bootinfo['control_panel'] = webnotes._dict(cp.copy())
bootinfo['sysdefaults'] = webnotes.defaults.get_defaults()
+ bootinfo['restrictions'] = webnotes.defaults.get_restrictions()
bootinfo['server_date'] = webnotes.utils.nowdate()
bootinfo["send_print_in_body_and_attachment"] = webnotes.conn.get_value("Email Settings",
None, "send_print_in_body_and_attachment")
diff --git a/webnotes/db.py b/webnotes/db.py
index a612260018..013a895209 100644
--- a/webnotes/db.py
+++ b/webnotes/db.py
@@ -414,14 +414,14 @@ class Database:
def get_global(self, key, user='__global'):
return self.get_default(key, user)
- def set_default(self, key, val, parent="Control Panel"):
+ def set_default(self, key, val, parent="Control Panel", parenttype=None):
"""set control panel default (tabDefaultVal)"""
import webnotes.defaults
- webnotes.defaults.set_default(key, val, parent)
+ webnotes.defaults.set_default(key, val, parent, parenttype)
- def add_default(self, key, val, parent="Control Panel"):
+ def add_default(self, key, val, parent="Control Panel", parenttype=None):
import webnotes.defaults
- webnotes.defaults.add_default(key, val, parent)
+ webnotes.defaults.add_default(key, val, parent, parenttype)
def get_default(self, key, parent="Control Panel"):
"""get default value"""
diff --git a/webnotes/defaults.py b/webnotes/defaults.py
index c9ccb1b815..6f50a58c9f 100644
--- a/webnotes/defaults.py
+++ b/webnotes/defaults.py
@@ -7,11 +7,11 @@ import memc
# User
-def set_user_default(key, value, user=None):
- set_default(key, value, user or webnotes.session.user)
+def set_user_default(key, value, user=None, parenttype=None):
+ set_default(key, value, user or webnotes.session.user, parenttype)
-def add_user_default(key, value, user=None):
- add_default(key, value, user or webnotes.session.user)
+def add_user_default(key, value, user=None, parenttype=None):
+ add_default(key, value, user or webnotes.session.user, parenttype)
def get_user_default(key, user=None):
d = get_defaults(user or webnotes.session.user).get(key, None)
@@ -20,7 +20,18 @@ def get_user_default(key, user=None):
def get_user_default_as_list(key, user=None):
d = get_defaults(user or webnotes.session.user).get(key, None)
return (not isinstance(d, list)) and [d] or d
-
+
+def get_restrictions():
+ if webnotes.local.restrictions is None:
+ out = {}
+ for key, value in webnotes.conn.sql("""select defkey, defvalue
+ from tabDefaultValue where parent=%s and parenttype='Restriction'""", webnotes.session.user):
+ out.setdefault(key, [])
+ out[key].append(value)
+
+ webnotes.local.restrictions = out
+ return webnotes.local.restrictions
+
def get_defaults(user=None):
if not user and webnotes.session:
user = webnotes.session.user
@@ -57,21 +68,21 @@ def get_global_default(key):
# Common
-def set_default(key, value, parent):
+def set_default(key, value, parent, parenttype="Control Panel"):
if webnotes.conn.sql("""select defkey from `tabDefaultValue` where
defkey=%s and parent=%s """, (key, parent)):
# update
- webnotes.conn.sql("""update `tabDefaultValue` set defvalue=%s
- where parent=%s and defkey=%s""", (value, parent, key))
+ webnotes.conn.sql("""update `tabDefaultValue` set defvalue=%s, parenttype=%s
+ where parent=%s and defkey=%s""", (value, parenttype, parent, key))
clear_cache(parent)
else:
add_default(key, value, parent)
-def add_default(key, value, parent):
+def add_default(key, value, parent, parenttype=None):
d = webnotes.doc({
"doctype": "DefaultValue",
"parent": parent,
- "parenttype": "Control Panel",
+ "parenttype": parenttype or "Control Panel",
"parentfield": "system_defaults",
"defkey": key,
"defvalue": value
@@ -124,28 +135,10 @@ def get_defaults_for(parent="Control Panel"):
elif d.defvalue is not None:
defaults[d.defkey] = d.defvalue
- if webnotes.session and parent == webnotes.session.user:
- defaults.update(get_defaults_for_match(defaults))
-
webnotes.cache().set_value("__defaults:" + parent, defaults)
return defaults
-def get_defaults_for_match(userd):
- """ if a profile based match condition exists for a user's role
- and no user property is specified for that match key,
- set default value as user's profile for that match key"""
- user_roles = webnotes.get_roles()
- out = {}
-
- for role, match in webnotes.conn.sql("""select distinct role, `match`
- from `tabDocPerm` where ifnull(permlevel, 0)=0 and `read`=1
- and `match` like "%:user" """):
- if role in user_roles and match.split(":")[0] not in userd:
- out[match.split(":")[0]] = webnotes.session.user
-
- return out
-
def clear_cache(parent=None):
def all_profiles():
return webnotes.conn.sql_list("select name from tabProfile") + ["Control Panel", "__global"]
diff --git a/webnotes/model/create_new.py b/webnotes/model/create_new.py
index 1fac653174..4a81a47dd9 100644
--- a/webnotes/model/create_new.py
+++ b/webnotes/model/create_new.py
@@ -20,6 +20,8 @@ def get_new_doc(doctype, parent_doc = None, parentfield = None):
meta = webnotes.get_doctype(doctype)
+ restrictions = webnotes.defaults.get_restrictions()
+
if parent_doc:
doc.parent = parent_doc.name
doc.parenttype = parent_doc.doctype
@@ -29,7 +31,11 @@ def get_new_doc(doctype, parent_doc = None, parentfield = None):
for d in meta.get({"doctype":"DocField", "parent": doctype}):
default = webnotes.defaults.get_user_default(d.fieldname)
- if default:
+
+ if (d.fieldtype=="Link") and d.ignore_restrictions != 1 and (d.options in restrictions)\
+ and len(restrictions[d.options])==1:
+ doc.fields[d.fieldname] = restrictions[d.options][0]
+ elif default:
doc.fields[d.fieldname] = default
elif d.fields.get("default"):
if d.default == "__user":
diff --git a/webnotes/widgets/form/load.py b/webnotes/widgets/form/load.py
index 573c39e6bd..23d40d7404 100644
--- a/webnotes/widgets/form/load.py
+++ b/webnotes/widgets/form/load.py
@@ -28,6 +28,9 @@ def getdoc(doctype, name, user=None):
try:
bean = webnotes.bean(doctype, name)
bean.run_method("onload")
+
+ if not bean.has_read_perm():
+ raise webnotes.PermissionError
doclist = bean.doclist
diff --git a/webnotes/widgets/reportview.py b/webnotes/widgets/reportview.py
index 5b04cb604b..b06bf52d20 100644
--- a/webnotes/widgets/reportview.py
+++ b/webnotes/widgets/reportview.py
@@ -187,52 +187,49 @@ def build_filter_conditions(filters, conditions):
def build_match_conditions(doctype, fields=None, as_condition=True):
"""add match conditions if applicable"""
-
match_filters = {}
match_conditions = []
- match = True
if not getattr(webnotes.local, "reportview_tables", None) \
or not getattr(webnotes.local, "reportview_doctypes", None):
webnotes.local.reportview_tables = get_tables(doctype, fields)
load_doctypes()
-
- if not getattr(webnotes.local, "reportview_roles", None):
- webnotes.local.reportview_roles = webnotes.get_roles()
-
- for d in webnotes.local.reportview_doctypes[doctype]:
- if d.doctype == 'DocPerm' and d.parent == doctype:
- if d.role in webnotes.local.reportview_roles:
- if d.match: # role applicable
- if ':' in d.match:
- document_key, default_key = d.match.split(":")
- else:
- default_key = document_key = d.match
- for v in webnotes.defaults.get_user_default_as_list(default_key, \
- webnotes.session.user) or ["** No Match **"]:
- if as_condition:
- match_conditions.append('`tab%s`.%s="%s"' % (doctype,
- document_key, v))
- else:
- if v:
- match_filters.setdefault(document_key, [])
- if v not in match_filters[document_key]:
- match_filters[document_key].append(v)
-
- elif d.read == 1 and d.permlevel == 0:
- # don't restrict if another read permission at level 0
- # exists without a match restriction
- match = False
- match_filters = {}
- if as_condition:
- conditions = ""
- if match_conditions and match:
- conditions = '('+ ' or '.join(match_conditions) +')'
+ # get restrictions
+ restrictions = webnotes.defaults.get_restrictions()
+
+ if not restrictions:
+ return "" if as_condition else {}
+
+ fields_to_check = webnotes.local.reportview_doctypes[doctype].get({"doctype":"DocField",
+ "fieldtype":"Link", "parent":doctype,
+ "ignore_restriction":("!=", 1),
+ "options":("in", restrictions.keys())})
+ if doctype in restrictions:
+ fields_to_check.append(webnotes._dict({"fieldname":"name", "options":doctype}))
+ # check in links
+ for df in fields_to_check:
+ if as_condition:
+ match_conditions.append('`tab{doctype}`.{fieldname} in ({values})'.format(doctype=doctype,
+ fieldname=df.fieldname,
+ values=", ".join([('"'+v.replace('"', '\"')+'"') for v in restrictions[df.options]])))
+ else:
+ match_filters.setdefault(df.fieldname, [])
+ match_filters[df.fieldname]= restrictions[df.options]
+
+ # add owner match
+ if webnotes.local.reportview_doctypes[doctype].get({"doctype":"DocPerm","read":1,
+ "permlevel":0,"match":"owner"}):
+ match_conditions.append('`tab{doctype}.owner="{user}"`'.format(doctye=doctype,
+ owner=webnotes.session.user))
+ match_filters["owner"] = [webnotes.session.user]
+
+ if as_condition:
+ conditions = " and ".join(match_conditions)
doctype_conditions = get_doctype_conditions(doctype)
if doctype_conditions:
- conditions += ' and ' + doctype_conditions if conditions else doctype_conditions
+ conditions += ' and ' + doctype_conditions if conditions else doctype_conditions
return conditions
else:
return match_filters