@@ -1211,6 +1211,115 @@ returns some data about the class::
12111211Adding other endpoints (e.g. to allow an OPTIONS query against
12121212``/data/issue/@schema``) is left as an exercise for the reader.
12131213
1214+ Controlling Access to Backend Data
1215+ ==================================
1216+
1217+ Roundup's schema is the primary access control mechanism. Roles and
1218+ Permissions provide the ability to carefully control what data can be
1219+ seen.
1220+
1221+ However the templating system can access the hyperdb directly which
1222+ allows filtering to happen with admin privs escaping the standard
1223+ permissions scheme. For example access to a user's roles should be
1224+ limited to the user (read only) and an admin. If you have customized
1225+ your schema to implement `Restricting the list of
1226+ users that are assignable to a task <customizing.html#restricting-the-list-of-users-that-are-assignable-to-a-task>`__ so that only users with a
1227+ Developer role are allowed to be assigned to an issue, a rest end
1228+ point must be added to provide a view that exposes users with this
1229+ permission.
1230+
1231+ Using the normal ``/data/user?roles=Developer`` will return all the
1232+ users in the system unless you are an admin user because most users
1233+ can't see the roles. Building on the `Adding new rest endpoints`_
1234+ section this code adds a new endpoint `/data/@permission/Developer`
1235+ that returns a list of users with the developer role::
1236+
1237+ from roundup.rest import Routing, RestfulInstance
1238+ from cgi import MiniFieldStorage
1239+
1240+ class RestfulInstance(object):
1241+
1242+ @Routing.route("/data/@permission/Developer")
1243+ def get_role_Developer(self, input):
1244+ '''An endpoint to return a list of users with Developer
1245+ role who can be assigned to an issue.
1246+
1247+ It ignores attempt to search by any property except
1248+ username and realname. It also ignores the whole @fields
1249+ specification if it specifies a property the user
1250+ can't view. Other @ query params (e.g. @page... and
1251+ @verbose) are supported.
1252+
1253+ It assumes admin access rights so that the roles property
1254+ of the user can be searched. This is needed if the roles
1255+ property is not searchable/viewable by normal users. A user
1256+ who can search roles can identify users with the admin
1257+ role. So it does not respond the same as a rest/data/users
1258+ search by a non-admin user.
1259+ '''
1260+ # get real user id
1261+ realuid=self.db.getuid()
1262+
1263+ def allowed_field(fs):
1264+ if fs.name in ['username', 'realname' ]:
1265+ # only allow search matches for these fields
1266+ return True
1267+ elif fs.name in [ '@fields' ]:
1268+ for prop in fs.value.split(','):
1269+ # if any property is unviewable to user, remove
1270+ # @field entry. If they can't see it for the admin
1271+ # user, don't let them see it for any user.
1272+ if not self.db.security.hasPermission(
1273+ 'View', realuid, 'user', property=prop,
1274+ itemid='1'):
1275+ return False
1276+ return True
1277+ elif fs.name.startswith("@"):
1278+ # allow @page..., @verbose etc.
1279+ return True
1280+
1281+ # deny all other url parmeters
1282+ return False
1283+
1284+ # Cleanup input.list to prevent user from probing roles
1285+ # or viewing things the user should not be able to view.
1286+ input.list[:] = [ fs for fs in input.list
1287+ if allowed_field(fs) ]
1288+
1289+ # Add the role filter required to implement the permission
1290+ # search
1291+ input.list.append(MiniFieldStorage("roles", "Developer"))
1292+
1293+ # change user to acquire permission to search roles
1294+ self.db.setCurrentUser('admin')
1295+
1296+ # Once we have cleaned up the request, pass it to
1297+ # get_collection as though /rest/data/users?... has been called
1298+ # to get @verbose and other args supported.
1299+ return self.get_collection('user', input)
1300+
1301+ Calling this with: `curl 'http://example.com/demo/rest/data/@permission/Developer?@fields=realname&roles=Users&@verbose=2'`
1302+ produces output similar to::
1303+
1304+ {
1305+ "data": {
1306+ "collection": [
1307+ {
1308+ "username": "agent",
1309+ "link": http://example.com/demo/rest/data/user/4",
1310+ "realname": "James Bond",
1311+ "id": "4"
1312+ }
1313+ ],
1314+ "@total_size": 1
1315+ }
1316+ }
1317+
1318+ assuming user 4 is the only user with the Developer role. Note that
1319+ the url passes the `roles=User` filter option which is silently
1320+ ignored.
1321+
1322+
12141323Creating Custom Rate Limits
12151324===========================
12161325
0 commit comments