Merge pull request #1682 from garyhgohoos/24187
authorGil Moskowitz <gmoskowitz@xtuple.com>
Fri, 18 Jul 2014 19:30:06 +0000 (15:30 -0400)
committerGil Moskowitz <gmoskowitz@xtuple.com>
Fri, 18 Jul 2014 19:30:06 +0000 (15:30 -0400)
Issue #24187:release pr using default itemsrc and add to unreleased po

enyo-client/application/source/en/strings.js
enyo-client/application/source/ext/datasource.js
enyo-client/application/source/views/workspace.js
enyo-client/database/orm/models/sys.json
node-datasource/lib/ext/models.js
node-datasource/routes/install_extension.js

index 182629f..4e86c70 100644 (file)
     "_subtotal": "Subtotal",
     "_suffix": "Suffix",
     "_summary": "Summary",
+    "_success!": "Success!",
     "_successors": "Successors",
     "_symbol": "Symbol",
     "_system": "System",
     "_deleteLine?": "Are you sure you want to delete this line?",
     "_exitPageWarning": "You are about to leave the xTuple application.",
     "_installExtensionWarning": "Extensions are very powerful and potentially have full access to your " +
-      "data. You should only install an extension from a source you trust.",
+      "data. You should only install an extension from a source you trust. ",
     "_insufficientPrivileges": "You have insufficient privileges to perform this action.",
     "_manualFreight": "Manually editing the freight will disable automatic freight recalculations.",
     "_mustSave": "You must save your changes before proceeding.",
index 8a216b1..554e05f 100644 (file)
@@ -206,7 +206,7 @@ white:true*/
                   XM.jsonpatch.apply(attrs, data.patches);
                   cModel.etag = data.etag;
 
-                  // This is a hack to work around Backbone messing with 
+                  // This is a hack to work around Backbone messing with
                   // attributes when we don't want it to. Parse function
                   // on model handles the other side of this
                   options.fixAttributes = cModel.attributes;
@@ -259,7 +259,8 @@ white:true*/
       // handle error
       if (inResponse.isError) {
         if (inSender.error) {
-          params.error = inResponse.message;
+          // inResponse.message sometimes gets lost in the vagaries of socket-io
+          params.error = inResponse.message || inResponse.errorMessage;
           error = XT.Error.clone('xt1001', { params: params });
           inSender.error.call(this, error);
         }
index 98860f5..f301ee0 100644 (file)
@@ -463,7 +463,7 @@ strict: false*/
             },
             {
               success: function (message) {
-                that.doNotify({message: message});
+                that.doNotify({message: message && message.loc()});
               },
               error: function (error) {
                 that.doNotify({message: error.message ? error.message() : error});
index 22e20d7..69b2d2c 100644 (file)
           "column": "grp_descrip"
         }
       },
+      {
+        "name": "grantedPrivileges",
+        "toMany": {
+          "type": "UserAccountRolePrivilegeAssignment",
+          "column": "grp_id",
+          "inverse": "userAccountRole",
+          "isNested": true
+        }
+      },
       {
         "name": "grantedExtensions",
         "toMany": {
     ],
     "isSystem": true
   },
+  {
+    "context": "xtuple",
+    "nameSpace": "SYS",
+    "type": "UserAccountRolePrivilegeAssignment",
+    "table": "grppriv",
+    "idSequenceName": "grppriv_grppriv_id_seq",
+    "comment": "User Account Role Privilege Assignment Map",
+    "privileges": {
+      "all": {
+        "create": true,
+        "read": true,
+        "update": false,
+        "delete": true
+      }
+    },
+    "properties": [
+      {
+        "name": "id",
+        "attr": {
+          "type": "Number",
+          "column": "grppriv_id",
+          "isPrimaryKey": true
+        }
+      },
+      {
+        "name": "uuid",
+        "attr": {
+          "type": "String",
+          "column": "obj_uuid",
+          "isNaturalKey": true
+        }
+      },
+      {
+        "name": "userAccountRole",
+        "attr": {
+          "type": "Number",
+          "column": "grppriv_grp_id"
+        }
+      },
+      {
+        "name": "privilege",
+        "toOne": {
+          "type": "Privilege",
+          "column": "grppriv_priv_id"
+        }
+      }
+    ],
+    "isNestedOnly": true,
+    "isSystem": true
+  },
   {
     "context": "xtuple",
     "nameSpace": "SYS",
index 83ac49a..b9d95c5 100644 (file)
@@ -1,11 +1,13 @@
-/*jshint indent:2, curly:true, eqeqeq:true, immed:true, latedef:true,
+/*jshint node:true, indent:2, curly:true, eqeqeq:true, immed:true, latedef:true,
 newcap:true, noarg:true, regexp:true, undef:true, strict:true, trailing:true,
 white:true*/
-/*global SYS:true, XM:true, Backbone:true, _:true */
+/*global SYS:true, XM:true, Backbone:true, _:true, X: true */
 
 (function () {
   "use strict";
 
+  var async = require("async");
+
   /**
     @class
 
@@ -106,8 +108,55 @@ white:true*/
   SYS.User = XM.SimpleModel.extend({
     /** @scope SYS.User.prototype */
 
-    recordType: 'SYS.User'
-
+    recordType: 'SYS.User',
+
+    /**
+      Checks for a user privilege. Also checks all the roles that the user is a part of.
+      Necessarily async because not all the relevant data is nested.
+      Not portable to the client because of the backbone-relational-lessness
+      of the models.
+      `callback(err, result)` where result is truthy iff the user has the privilege
+    */
+    checkPrivilege: function (privName, database, callback) {
+      var privCheck = _.find(this.get("grantedPrivileges"), function (model) {
+        return model.privilege === privName;
+      });
+      if (privCheck) {
+        callback(); // the user has this privilege!
+        return;
+      }
+      // this gets a little dicey: check all the user's roles for the priv, which
+      // requires async.map
+      var roles = _.map(this.get("grantedUserAccountRoles"), function (grantedRole) {
+        return grantedRole.userAccountRole;
+      });
+      var checkRole = function (roleName, next) {
+        var role = new SYS.UserAccountRole();
+        role.fetch({
+          id: roleName,
+          username: X.options.databaseServer.user,
+          database: database,
+          success: function (roleModel, results) {
+            var rolePriv = _.find(roleModel.get("grantedPrivileges"), function (grantedPriv) {
+              return grantedPriv.privilege === privName;
+            });
+            next(null, rolePriv);
+          }
+        });
+      };
+      async.map(roles, checkRole, function (err, results) {
+        // if any of the roles give the priv, then the user has the priv
+        var result = _.reduce(results, function (memo, priv) {
+          return priv || memo;
+        }, false);
+        console.log(result);
+        if (err || !result) {
+          callback({message: "_insufficientPrivileges"});
+          return;
+        }
+        callback(); // success!
+      });
+    }
   });
 
   /**
index 5dafe61..7a68505 100644 (file)
@@ -27,19 +27,11 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
           id: username,
           username: X.options.databaseServer.user,
           database: database,
-          success: function (model, results) {
-            // TODO: also check role-granted privileges
-            var privCheck = _.find(model.get("grantedPrivileges"), function (model) {
-              return model.privilege === "InstallExtension";
-            });
-            if (!privCheck) {
-              callback({message: "_insufficientPrivileges"});
-              return;
-            }
-            callback(); // success!
+          success: function (userModel, results) {
+            userModel.checkPrivilege("InstallExtension", database, callback);
           },
           error: function () {
-            callback({message: "_restoreError"});
+            callback({message: "_privilegeCheckError"});
           }
         });
       },
@@ -69,13 +61,13 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
       buildExtension
     ], function (err, results) {
       if (err) {
-        console.log(err);
         err.isError = true;
+        err.errorMessage = err.message;
         res.send(err);
         return;
       }
       console.log("all done");
-      res.send({data: "_success"});
+      res.send({data: "_success!"});
     });
   };
 }());