"_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.",
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;
// 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);
}
},
{
success: function (message) {
- that.doNotify({message: message});
+ that.doNotify({message: message && message.loc()});
},
error: function (error) {
that.doNotify({message: error.message ? error.message() : error});
"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",
-/*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
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!
+ });
+ }
});
/**
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"});
}
});
},
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!"});
});
};
}());