Permalink
Browse files

First implementation of the policy system

  • Loading branch information...
1 parent 6946a82 commit 252cc45beb1c036f805ba471fbfc0f6d4c7445a6 @mauritslamers committed Aug 11, 2010
Showing with 251 additions and 77 deletions.
  1. +1 −2 OrionFileAuth.js
  2. +71 −4 OrionPolicies.js
  3. +10 −1 OrionPolicyModel.js
  4. +159 −68 OrionServer.js
  5. +10 −2 policies/sample.js
View
@@ -20,8 +20,7 @@ global.OrionFileAuth = OrionAuth.extend({
// checkAuth has to return immediately, it should not be done using a callback
checkAuth: function(user,passwd,passwdIsMD5){
if(!this._authData){
- var fn = ['./',this.fileName].join('');
- var data = require(fn); // the data should already be in the right format
+ var data = require(this.fileName); // the data should already be in the right format
this._authData = data.users;
}
// so the auth data is loaded, do a check
View
@@ -23,6 +23,8 @@ global.OrionPolicies = SC.Object.extend({
policyFile: null, // the file defining the enabled policies
+ filterRecords: NO, // whether a record should be filtered by the server when creating a new record or updating an existing one
+
policyCache: null, // where the policy rule objects are cached
// prevent a policy check if the users role is one of these roles, if a noPolicyCheckForRoles is defined in the
@@ -53,16 +55,81 @@ global.OrionPolicies = SC.Object.extend({
}
},
- checkPolicy: function(storeRequest,user,record,callback){
+ _tmpRecordCache: {},
+ _tmpRecordCacheCount: {},
+
+ // this seems a bit complicated, but we have to be aware that multiple requests can coincide, so we have to separate
+ // all different requests, for which we use cache keys. the function expects the checkPolicy function to
+ // create the array on the _tmpRecordCache first
+ // it is even more complicated than I thought at first
+ createPolicyCheckCallback: function(record,cacheKey,callback){
+ var me = this;
+ return function(val){
+ if(val === YES){ // no changes to the record
+ me._tmpRecordCache[cacheKey].push(record);
+ }
+ if(val && val !== 'retry') me._tmpRecordCache[cacheKey].push(val); // anything else, not being NO or retry, so push the updated record
+ me._tmpRecordCacheCount[cacheKey]--;
+ if(me._tmpRecordCacheCount[cacheKey] === 0){
+ // if this is the last record, send the entire array to the callback
+ var records = me._tmpRecordCache[cacheKey];
+ delete me._tmpRecordCache[cacheKey]; // delete the old contents
+ delete me._tmpRecordCacheCount[cacheKey];
+ callback(records);
+ }
+ };
+ },
+
+ generateCacheKey: function(){
+ // the idea for this method was copied from the php site:
+ // http://www.php.net/manual/en/function.session-regenerate-id.php#60478
+ var keyLength = 32,
+ keySource = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ keySourceLength = keySource.length + 1, // we need to add one, to make sure the last character will be used in generating the key
+ ret = [],
+ curCharIndex = 0;
+
+ for(var i=0;i<=keyLength;i++){
+ curCharIndex = Math.floor(Math.random()*keySourceLength);
+ ret.push(keySource[curCharIndex]);
+ }
+ return ret.join('');
+ },
+
+ checkPolicy: function(storeRequest,record,callback){
// function to check whether the current record and storeRequest are allowed...
var resource = storeRequest.bucket;
var action = storeRequest.action;
var policies = this.get('policyCache');
var noPolCheck = this.get('noPolicyCheckForRoles');
- if(noPolCheck.indexOf(user.role) === -1){
- policies[resource][action](storeRequest,user,record,callback);
+ if(noPolCheck.indexOf(storeRequest.userData.role) === -1){
+ // check whether record happens to be an array, we have to pass all records through the policy check
+ // which is kind of difficult, and most importantly, ALL policy checks MUST call the callback, otherwise
+ // the data will never arrive at the client!!
+ if(record instanceof Array){
+ var me = this, cacheKey = this.generateCacheKey();
+ var curRec, polCheck;
+ if(record.length !== 0){
+ this._tmpRecordCache[cacheKey]=[];
+ this._tmpRecordCacheCount[cacheKey]=record.length;
+ for(var i=0,len=record.length;i<len;i++){
+ curRec = record[i];
+ polCheck = policies[resource][action];
+ polCheck(storeRequest,storeRequest.client.userData,curRec,this.createPolicyCheckCallBack(curRec,cacheKey,callback));
+ }
+ }
+ }
+ else {
+ policies[resource][action](storeRequest,storeRequest.client.userData,record,callback);
+ }
}
- else callback(YES); // if the users role is in the noPolicyCheck, just allow
+ else callback(YES); // if the users role is in the noPolicyCheck, just allow. Otherwise the policy settings take over (which is default not to send anything).
+ },
+
+ filterRecord: function(storeRequest,record){
+ var resource = storeRequest.bucket, action = storeRequest.action;
+ var policies = this.get('policyCache');
+ return policies[resource][action](storeRequest, storeRequest.client.userData,record);
}
});
View
@@ -25,7 +25,7 @@ global.OrionPolicyModel = SC.Object.extend({
This function can be called twice, once before the db request and once after the db request.
The difference can be detected by the record value being undefined.
Whether it is called after the db request depends on the return value of the first call.
- Accepted callback value is either YES, NO or a filtered record
+ Accepted callback value is either YES, NO, 'retry' or a filtered record
*/
refresh: function(storeRequest,user,record,callback){
callback(this.defaultResponse);
@@ -37,5 +37,14 @@ global.OrionPolicyModel = SC.Object.extend({
*/
destroy: function(storeRequest,user,record,callback){
callback(this.defaultResponse);
+ },
+
+ /*
+ This function can be used by the other functions above, but also needs to be
+ available separately to be able to filter data.
+ In contrast with the other functions, this function should return immediately
+ */
+ filter: function(storeRequest,user,record){
+ return record;
}
});
Oops, something went wrong.

0 comments on commit 252cc45

Please sign in to comment.