/*jslint plusplus: true, devel: true, nomen: true, node: true, indent: 4, maxerr: 50 */
"use strict";
var Strings = require("../strings"),
util = require("../util"),
async = require("async");
/**
* Constructor for Memory rule store.
*
* This store keeps rules in memory only.
* Rules are not persisted across application reloads and therefore they have to be loaded early in the application start (initialization) phase.
*
* This is the default store for authority and it doesn’t have to be explicitly configured unless it is used in conjunction with other stores.
*
* @example
* var authority = require("authority");
*
* authority.setRules([{
* name: "allow_managers_only",
description: "Only managers can pass.",
condition: { title: "Manager" }
* }, {
* name: "allow_teenagers_only",
description: "Only users between 13 and 19 years old can pass.",
condition: { age: { $gte: 13, $lte: 19 } }
* }
* ]);
*
* @class Represents a Memory rule store.
*/
function MemoryStore() {
/**
* The name of this store. The default value is "memory". NOTE: every store is required to have a unique name within a manager.
*/
this.name = "memory";
/**
* The object that holds the references to all stored rules. NOTE: this member is not part of the Authority API and it is specific to this store only.
*/
this.rules = {};
}
/**
* Adds one or more rules to the store.
*
* @param {(object|object[])} rules - The rule or an array of rules to be added to the store.
*
* @param {boolean=} override - Specifies whether existing rules should be replaced by the ones provided to this method. Rules are matched by their name.
* If this parameter is not specified or is false and there is a name conflict an error will be returned or thrown depending on the usage of the method.
*
* @param {function=} done - Optional callback function that will be called after the rule(s) have been added.
* If the method is unsuccessful an error object will be passed as a single parameter to the callback.
* If the callback is omitted and the method is unsuccessful an error will be thrown.
*
* @return {null}
*/
MemoryStore.prototype.setRules = function (rules, override, done) {
var that = this;
if (typeof override === "function") {
done = override;
override = null;
}
if (!done) {
done = function (err) {
if (err) {
throw err;
}
};
}
function setRule(rule, cb) {
if (!override && that.rules[rule.name]) {
return cb(new Error(Strings.ERR_RULE_EXISTS));
}
that.rules[rule.name] = rule;
cb();
}
if (Array.isArray(rules)) {
async.each(rules, function (item, callback) {
setRule(item, callback);
}, done);
} else {
setRule(rules, done);
}
};
/**
* Retrieves the specified rule from the store.
*
* @param {string} name - The name of the rule.
* @param {function} done - The callback that handles the result.
* The first parameter will always be null for this store while the second parameter will contain the requested rule or null if not found.
* @return {null}
*/
MemoryStore.prototype.getRule = function (name, done) {
done(null, this.rules[name]);
};
/**
* Removes the specified rules from the store.
*
* @param {(string|string[])} names - The name or an array of names to remove.
* @param {function=} done - Optional callback function that will be called after the rule(s) have been deleted.
* If the method is unsuccessful an error object will be passed as a single parameter to the callback.
* If the callback is omitted and the method is unsuccessful an error will be thrown.
* @return {null}
*/
MemoryStore.prototype.deleteRules = function (names, done) {
var that = this;
if (!done) {
done = function (err) {
if (err) {
throw err;
}
};
}
function remRule(name, cb) {
delete that.rules[name];
cb();
}
if (Array.isArray(names)) {
async.each(names, function (item, callback) {
remRule(item, callback);
}, done);
} else {
remRule(names, done);
}
};
/**
* Retrieves the names and descriptions of the rules present in the store and matching the specified criteria.
*
* All parameters are required.
*
* @param {number} start - The index at wich to begin retrieving names. The index of the first element is 0.
* @param {number} count - The number of names to retrieve. `0` denotes all remaining.
* @param {string} match - Wildcard expression to match names. `null` denotes any name. Example: `allow_*`
* @param {function} done - The callback that handles the result.
* The first parameter will always be null for this store while the second parameter will contain an array of matched objects `{ name: "string", description: "string" }`.
*/
MemoryStore.prototype.getRuleNames = function (start, count, match, done) {
var keys = Object.keys(this.rules),
res = [],
that = this,
regex;
if (match) {
regex = util.regExpFromWildExp(match);
keys = keys.filter(function (el) {
return regex.test(el);
});
}
if (start || count) {
if (count) {
count = start + count;
} else {
count = undefined;
}
keys = keys.splice(start, count);
}
keys.forEach(function (key) {
var rule = that.rules[key];
res.push({ name: rule.name, description: rule.description });
});
done(null, res);
};
/**
* Gets the total count of rules present in the store.
*
* @param {function} done - The callback that handles the result.
* The first parameter will always be null for this store while the second parameter will contain a number representing the total count of rules.
*/
MemoryStore.prototype.getRuleCount = function (done) {
done(null, Object.keys(this.rules).length);
};
module.exports = MemoryStore;