mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-28 21:33:24 +03:00
Refactored permissions.canThis to class structure
- updates the file to modern classes given that Ghost/Node supports them
This commit is contained in:
parent
28a69ec5c6
commit
120dee86b3
@ -12,186 +12,179 @@ const messages = {
|
||||
noActionsMapFoundError: 'No actions map found, ensure you have loaded permissions into database and then call permissions.init() before use.'
|
||||
};
|
||||
|
||||
let canThis;
|
||||
let CanThisResult;
|
||||
|
||||
// Base class for canThis call results
|
||||
CanThisResult = function () {
|
||||
};
|
||||
|
||||
CanThisResult.prototype.buildObjectTypeHandlers = function (objTypes, actType, context, permissionLoad) {
|
||||
const objectTypeModelMap = {
|
||||
post: models.Post,
|
||||
role: models.Role,
|
||||
user: models.User,
|
||||
permission: models.Permission,
|
||||
setting: models.Settings,
|
||||
invite: models.Invite,
|
||||
integration: models.Integration,
|
||||
comment: models.Comment
|
||||
};
|
||||
|
||||
// Iterate through the object types, i.e. ['post', 'tag', 'user']
|
||||
return _.reduce(objTypes, function (objTypeHandlers, objType) {
|
||||
// Grab the TargetModel through the objectTypeModelMap
|
||||
const TargetModel = objectTypeModelMap[objType];
|
||||
|
||||
// Create the 'handler' for the object type;
|
||||
// the '.post()' in canThis(user).edit.post()
|
||||
objTypeHandlers[objType] = function (modelOrId, unsafeAttrs) {
|
||||
let modelId;
|
||||
unsafeAttrs = unsafeAttrs || {};
|
||||
|
||||
// If it's an internal request, resolve immediately
|
||||
if (context.internal) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (_.isNumber(modelOrId) || _.isString(modelOrId)) {
|
||||
// It's an id already, do nothing
|
||||
modelId = modelOrId;
|
||||
} else if (modelOrId) {
|
||||
// It's a model, get the id
|
||||
modelId = modelOrId.id;
|
||||
}
|
||||
// Wait for the user loading to finish
|
||||
return permissionLoad.then(function (loadedPermissions) {
|
||||
// Iterate through the user permissions looking for an affirmation
|
||||
const userPermissions = loadedPermissions.user ? loadedPermissions.user.permissions : null;
|
||||
const apiKeyPermissions = loadedPermissions.apiKey ? loadedPermissions.apiKey.permissions : null;
|
||||
const memberPermissions = loadedPermissions.member ? loadedPermissions.member.permissions : null;
|
||||
|
||||
let hasUserPermission;
|
||||
let hasApiKeyPermission;
|
||||
let hasMemberPermission = false;
|
||||
|
||||
const checkPermission = function (perm) {
|
||||
let permObjId;
|
||||
|
||||
// Look for a matching action type and object type first
|
||||
if (perm.get('action_type') !== actType || perm.get('object_type') !== objType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Grab the object id (if specified, could be null)
|
||||
permObjId = perm.get('object_id');
|
||||
|
||||
// If we didn't specify a model (any thing)
|
||||
// or the permission didn't have an id scope set
|
||||
// then the "thing" has permission
|
||||
if (!modelId || !permObjId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, check if the id's match
|
||||
// TODO: String vs Int comparison possibility here?
|
||||
return modelId === permObjId;
|
||||
};
|
||||
|
||||
if (loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Owner'})) {
|
||||
hasUserPermission = true;
|
||||
} else if (!_.isEmpty(userPermissions)) {
|
||||
hasUserPermission = _.some(userPermissions, checkPermission);
|
||||
}
|
||||
|
||||
if (loadedPermissions.member) {
|
||||
hasMemberPermission = _.some(memberPermissions, checkPermission);
|
||||
}
|
||||
|
||||
// Check api key permissions if they were passed
|
||||
hasApiKeyPermission = true;
|
||||
if (!_.isNull(apiKeyPermissions)) {
|
||||
// api key request have no user, but we want the user permissions checks to pass
|
||||
hasUserPermission = true;
|
||||
hasApiKeyPermission = _.some(apiKeyPermissions, checkPermission);
|
||||
}
|
||||
|
||||
// Offer a chance for the TargetModel to override the results
|
||||
if (TargetModel && _.isFunction(TargetModel.permissible)) {
|
||||
return TargetModel.permissible(
|
||||
modelId, actType, context, unsafeAttrs, loadedPermissions, hasUserPermission, hasApiKeyPermission, hasMemberPermission
|
||||
);
|
||||
}
|
||||
|
||||
if (hasUserPermission && hasApiKeyPermission) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NoPermissionError({message: tpl(messages.noPermissionToAction)}));
|
||||
});
|
||||
class CanThisResult {
|
||||
buildObjectTypeHandlers(objTypes, actType, context, permissionLoad) {
|
||||
const objectTypeModelMap = {
|
||||
post: models.Post,
|
||||
role: models.Role,
|
||||
user: models.User,
|
||||
permission: models.Permission,
|
||||
setting: models.Settings,
|
||||
invite: models.Invite,
|
||||
integration: models.Integration,
|
||||
comment: models.Comment
|
||||
};
|
||||
|
||||
return objTypeHandlers;
|
||||
}, {});
|
||||
};
|
||||
// Iterate through the object types, i.e. ['post', 'tag', 'user']
|
||||
return _.reduce(objTypes, function (objTypeHandlers, objType) {
|
||||
// Grab the TargetModel through the objectTypeModelMap
|
||||
const TargetModel = objectTypeModelMap[objType];
|
||||
|
||||
CanThisResult.prototype.beginCheck = function (context) {
|
||||
const self = this;
|
||||
let userPermissionLoad;
|
||||
let apiKeyPermissionLoad;
|
||||
let memberPermissionLoad;
|
||||
let permissionsLoad;
|
||||
// Create the 'handler' for the object type;
|
||||
// the '.post()' in canThis(user).edit.post()
|
||||
objTypeHandlers[objType] = function (modelOrId, unsafeAttrs) {
|
||||
let modelId;
|
||||
unsafeAttrs = unsafeAttrs || {};
|
||||
|
||||
// Get context.user, context.api_key and context.app
|
||||
context = parseContext(context);
|
||||
// If it's an internal request, resolve immediately
|
||||
if (context.internal) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (actionsMap.empty()) {
|
||||
throw new errors.InternalServerError({message: tpl(messages.noActionsMapFoundError)});
|
||||
if (_.isNumber(modelOrId) || _.isString(modelOrId)) {
|
||||
// It's an id already, do nothing
|
||||
modelId = modelOrId;
|
||||
} else if (modelOrId) {
|
||||
// It's a model, get the id
|
||||
modelId = modelOrId.id;
|
||||
}
|
||||
// Wait for the user loading to finish
|
||||
return permissionLoad.then(function (loadedPermissions) {
|
||||
// Iterate through the user permissions looking for an affirmation
|
||||
const userPermissions = loadedPermissions.user ? loadedPermissions.user.permissions : null;
|
||||
const apiKeyPermissions = loadedPermissions.apiKey ? loadedPermissions.apiKey.permissions : null;
|
||||
const memberPermissions = loadedPermissions.member ? loadedPermissions.member.permissions : null;
|
||||
|
||||
let hasUserPermission;
|
||||
let hasApiKeyPermission;
|
||||
let hasMemberPermission = false;
|
||||
|
||||
const checkPermission = function (perm) {
|
||||
let permObjId;
|
||||
|
||||
// Look for a matching action type and object type first
|
||||
if (perm.get('action_type') !== actType || perm.get('object_type') !== objType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Grab the object id (if specified, could be null)
|
||||
permObjId = perm.get('object_id');
|
||||
|
||||
// If we didn't specify a model (any thing)
|
||||
// or the permission didn't have an id scope set
|
||||
// then the "thing" has permission
|
||||
if (!modelId || !permObjId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, check if the id's match
|
||||
// TODO: String vs Int comparison possibility here?
|
||||
return modelId === permObjId;
|
||||
};
|
||||
|
||||
if (loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Owner'})) {
|
||||
hasUserPermission = true;
|
||||
} else if (!_.isEmpty(userPermissions)) {
|
||||
hasUserPermission = _.some(userPermissions, checkPermission);
|
||||
}
|
||||
|
||||
if (loadedPermissions.member) {
|
||||
hasMemberPermission = _.some(memberPermissions, checkPermission);
|
||||
}
|
||||
|
||||
// Check api key permissions if they were passed
|
||||
hasApiKeyPermission = true;
|
||||
if (!_.isNull(apiKeyPermissions)) {
|
||||
// api key request have no user, but we want the user permissions checks to pass
|
||||
hasUserPermission = true;
|
||||
hasApiKeyPermission = _.some(apiKeyPermissions, checkPermission);
|
||||
}
|
||||
|
||||
// Offer a chance for the TargetModel to override the results
|
||||
if (TargetModel && _.isFunction(TargetModel.permissible)) {
|
||||
return TargetModel.permissible(
|
||||
modelId, actType, context, unsafeAttrs, loadedPermissions, hasUserPermission, hasApiKeyPermission, hasMemberPermission
|
||||
);
|
||||
}
|
||||
|
||||
if (hasUserPermission && hasApiKeyPermission) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Promise.reject(new errors.NoPermissionError({message: tpl(messages.noPermissionToAction)}));
|
||||
});
|
||||
};
|
||||
|
||||
return objTypeHandlers;
|
||||
}, {});
|
||||
}
|
||||
|
||||
// Kick off loading of user permissions if necessary
|
||||
if (context.user) {
|
||||
userPermissionLoad = providers.user(context.user);
|
||||
} else {
|
||||
// Resolve null if no context.user to prevent db call
|
||||
userPermissionLoad = Promise.resolve(null);
|
||||
}
|
||||
beginCheck(context) {
|
||||
const self = this;
|
||||
let userPermissionLoad;
|
||||
let apiKeyPermissionLoad;
|
||||
let memberPermissionLoad;
|
||||
let permissionsLoad;
|
||||
|
||||
// Kick off loading of api key permissions if necessary
|
||||
if (context.api_key) {
|
||||
apiKeyPermissionLoad = providers.apiKey(context.api_key.id);
|
||||
} else {
|
||||
// Resolve null if no context.api_key
|
||||
apiKeyPermissionLoad = Promise.resolve(null);
|
||||
}
|
||||
// Get context.user, context.api_key and context.app
|
||||
context = parseContext(context);
|
||||
|
||||
if (context.member) {
|
||||
memberPermissionLoad = providers.member(context.member.id);
|
||||
}
|
||||
if (actionsMap.empty()) {
|
||||
throw new errors.InternalServerError({message: tpl(messages.noActionsMapFoundError)});
|
||||
}
|
||||
|
||||
// Wait for both user and app permissions to load
|
||||
permissionsLoad = Promise.all([userPermissionLoad, apiKeyPermissionLoad, memberPermissionLoad]).then(function (result) {
|
||||
return {
|
||||
user: result[0],
|
||||
apiKey: result[1],
|
||||
member: result[2]
|
||||
};
|
||||
});
|
||||
// Kick off loading of user permissions if necessary
|
||||
if (context.user) {
|
||||
userPermissionLoad = providers.user(context.user);
|
||||
} else {
|
||||
// Resolve null if no context.user to prevent db call
|
||||
userPermissionLoad = Promise.resolve(null);
|
||||
}
|
||||
|
||||
// Iterate through the actions and their related object types
|
||||
_.each(actionsMap.getAll(), function (objTypes, actType) {
|
||||
// Build up the object type handlers;
|
||||
// the '.post()' parts in canThis(user).edit.post()
|
||||
const objTypeHandlers = self.buildObjectTypeHandlers(objTypes, actType, context, permissionsLoad);
|
||||
// Kick off loading of api key permissions if necessary
|
||||
if (context.api_key) {
|
||||
apiKeyPermissionLoad = providers.apiKey(context.api_key.id);
|
||||
} else {
|
||||
// Resolve null if no context.api_key
|
||||
apiKeyPermissionLoad = Promise.resolve(null);
|
||||
}
|
||||
|
||||
// Define a property for the action on the result;
|
||||
// the '.edit' in canThis(user).edit.post()
|
||||
Object.defineProperty(self, actType, {
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
value: objTypeHandlers
|
||||
if (context.member) {
|
||||
memberPermissionLoad = providers.member(context.member.id);
|
||||
}
|
||||
|
||||
// Wait for both user and app permissions to load
|
||||
permissionsLoad = Promise.all([userPermissionLoad, apiKeyPermissionLoad, memberPermissionLoad]).then(function (result) {
|
||||
return {
|
||||
user: result[0],
|
||||
apiKey: result[1],
|
||||
member: result[2]
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
// Return this for chaining
|
||||
return this;
|
||||
};
|
||||
// Iterate through the actions and their related object types
|
||||
_.each(actionsMap.getAll(), function (objTypes, actType) {
|
||||
// Build up the object type handlers;
|
||||
// the '.post()' parts in canThis(user).edit.post()
|
||||
const objTypeHandlers = self.buildObjectTypeHandlers(objTypes, actType, context, permissionsLoad);
|
||||
|
||||
canThis = function (context) {
|
||||
// Define a property for the action on the result;
|
||||
// the '.edit' in canThis(user).edit.post()
|
||||
Object.defineProperty(self, actType, {
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
value: objTypeHandlers
|
||||
});
|
||||
});
|
||||
|
||||
// Return this for chaining
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function canThis(context) {
|
||||
const result = new CanThisResult();
|
||||
|
||||
return result.beginCheck(context);
|
||||
};
|
||||
|
||||
module.exports = canThis;
|
||||
|
Loading…
Reference in New Issue
Block a user