Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

pintura

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pintura - npm Package Compare versions

Comparing version 0.3.6 to 0.3.8

jsgi/access.js

4

jsgi/csrf.js

@@ -8,4 +8,5 @@ /**

nextApp = customHeader;
// default to this common header
customHeader = "x-requested-with";
}
customHeader = customHeader || "x-requested-with";
return function(request){

@@ -15,2 +16,3 @@ var headers = request.headers;

(request.method == "POST" && headers.referer && headers.referer.indexOf(headers.host + '/') > 0) ||
(headers.stream && headers.origin && headers.origin.indexOf(headers.host) > 0) ||
(request.method != "GET" && request.method != "POST"))){

@@ -17,0 +19,0 @@ request.crossSiteForgeable = true;

@@ -12,3 +12,3 @@ /**

exports.ErrorHandler = function(nextApp){
exports = module.exports = function(nextApp){
return function(request){

@@ -77,1 +77,3 @@ try{

};
exports.ErrorHandler = exports;

@@ -8,3 +8,3 @@ /**

var httpParamRegex = /^http[_-]/;
exports.HttpParams = function(nextApp){
exports = module.exports = function(nextApp){
return function(request){

@@ -37,1 +37,3 @@ var parts = request.queryString.split("&");

};
// back-compat property reference
exports.HttpParams = exports;

@@ -5,3 +5,3 @@ /**

var when = require("promised-io/promise").when;
exports.Metadata = function(nextApp){
module.exports = function(nextApp){
return function(request){

@@ -57,3 +57,5 @@ var metadata;

};
}
};
// back-compat property access
module.exports.Metadata = module.exports;

@@ -60,0 +62,0 @@ /*

@@ -13,6 +13,4 @@ /**

function dir(){var sys=require('sys');for(var i=0,l=arguments.length;i<l;i++)sys.debug(sys.inspect(arguments[i]));}
exports.RestStore = function(options){
module.exports = function(options){
return function(request){
// N.B. in async, options.getDataModel() can be a promise, so have to wait for it
return when(options.getDataModel(request), function(model){

@@ -167,1 +165,3 @@ var path = request.pathInfo.substring(1);

};
// back-compat property access
module.exports.RestStore = module.exports;

@@ -17,3 +17,3 @@ /*

//function dir(){var sys=require('sys');for(var i=0,l=arguments.length;i<l;i++)sys.debug(sys.inspect(arguments[i]));}
exports.Routes = function(customRoutes, nextApp){
exports = module.exports = function(customRoutes, nextApp){
// append custom routes

@@ -61,3 +61,4 @@ for (var i = 0, l = customRoutes.length; i < l; ++i) {

};
// back-compat property access
exports.Routes = exports;
function declare(method, regexp, handler, args){

@@ -64,0 +65,0 @@ if (typeof regexp === 'string') {

@@ -14,3 +14,3 @@ /**

exports.Session = function(options, nextApp){
exports = module.exports = function(options, nextApp){
// assign defaults

@@ -77,3 +77,4 @@ if (!options) options = {};

};
// back-compat property reference
exports.Session = exports;
// gets a session, creating a new one if necessary

@@ -80,0 +81,0 @@ exports.forceSession = function(request, expires){

{
"name": "pintura",
"version": "0.3.6",
"version": "0.3.8",
"author": "Kris Zyp",
"email": "kriszyp@gmail.com",
"description": "JSGI-based RESTful JSON/JavaScript server",
"contributors": ["Vladimir Dronnikov <dronnikov@gmail.com>"],
"contributors": [
"Vladimir Dronnikov <dronnikov@gmail.com>"
],
"keywords": [

@@ -14,4 +16,4 @@ "rest",

"persevere"
],
"mappings":{
],
"mappings": {
"perstore": "http://github.com/kriszyp/perstore/zipball/v0.2.4",

@@ -39,14 +41,14 @@ "templify": "http://github.com/dmachi/templify/zipball/master",

"licenses": [
{
"type": "AFLv2.1",
"url": "http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43"
},
{
"type": "BSD",
"url": "http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13"
}
{
"type": "AFLv2.1",
"url": "http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43"
},
{
"type": "BSD",
"url": "http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13"
}
],
"repository": {
"type":"git",
"url":"http://github.com/persvr/pintura"
"type": "git",
"url": "http://github.com/persvr/pintura"
},

@@ -57,16 +59,16 @@ "directories": {

"maintainers": [
{
"name": "Kris Zyp",
"email": "kriszyp@gmail.com"
}
{
"name": "Kris Zyp",
"email": "kriszyp@gmail.com"
}
],
"dependencies":{
"tunguska": ">=0.3.0",
"rql": ">=0.3.1",
"websocket-server": ">=1.4.01",
"dependencies": {
"formidable": ">=1.0.0",
"jsgi-node": ">=0.3.2",
"perstore": ">=0.3.0",
"promised-io": ">=0.3.0",
"formidable": ">=1.0.0",
"rql": ">=0.3.1",
"templify": ">=0.9.0",
"jsgi-node": ">=0.2.5"
"tunguska": ">=0.3.0",
"ws": "^0.4.32"
},

@@ -73,0 +75,0 @@ "devDependencies": {

@@ -7,85 +7,85 @@ /**

// load the default media types
require("./media/json");
require("./media/javascript");
require("./media/url-encoded");
require("./media/atom");
require("./media/multipart-form-data");
require("./media/html");
require("./media/uri-list");
require("./media/plain");
require("./media/message/json");
require('./media/json');
require('./media/javascript');
require('./media/url-encoded');
require('./media/atom');
require('./media/multipart-form-data');
require('./media/html');
require('./media/uri-list');
require('./media/plain');
require('./media/message/json');
var configure = require('./jsgi/configure');
var deepCopy = require('perstore/util/copy').deepCopy;
var config = exports.config = {
mediaSelector: require("./media").Media.optimumMedia,
database: require("perstore/stores"),
security: require("./security").DefaultSecurity(),
responseCache: require("perstore/store/memory").Memory({path: "response"}), //require("perstore/store/filesystem").FileSystem("response", {defaultExtension: "cache",dataFolder: "cache" }),
serverName: "Pintura",
mediaSelector: require('./media').Media.optimumMedia,
database: require('perstore/stores'),
security: require('./security').DefaultSecurity(),
responseCache: require('perstore/store/memory').Memory({path: 'response'}), //require('perstore/store/filesystem').FileSystem('response', {defaultExtension: 'cache',dataFolder: 'cache' }),
serverName: 'Pintura',
customRoutes: [],
getDataModel: function(request){
groups: {
public: [null],
user: '*',
admin: ['admin'],
},
getDataModel: function(request){
return exports.getDataModel(request);
}
};
exports.getDataModel = function(){
throw new Error("You must assign a getDataModel method to the pintura config object in order to expose data");
exports.configure = function(newConfig){
// copy new configuration options into the config object
deepCopy(newConfig, config);
}
exports.getDataModel = function(request){
// this is a simple default model
return request.dataModel;
};
exports.app = JsgiApp(null, config);
exports.registerModels = config.security.registerModels;
function JsgiApp(nextApp, config){
exports.app = configure([
// This is the set of JSGI middleware and appliance that comprises the Pintura
// request handling framework.
return require("./jsgi/context").SetContext({},
// We detect if the request could have been forged from another site
require("./jsgi/csrf").CSRFDetect(
// Support handling various cross-site request mechanisms like JSONP, window.name, CS-XHR
require("./jsgi/xsite").CrossSite(
// Handle header emulation through query parameters (useful for cross-site and links)
require("./jsgi/http-params").HttpParams(
// Handle HEAD requests
require("./jsgi/head").Head(
// Add some useful headers
require("./jsgi/pintura-headers").PinturaHeaders(config.serverName,
// Handle conditional requests
require("./jsgi/conditional").Conditional(true,
// Handle response conneg, converting from JS objects to byte representations
require("./jsgi/media").Serialize(config.mediaSelector,
// Handle errors that are thrown, converting to appropriate status codes
require("./jsgi/error").ErrorHandler(
// Handle transactions
require("perstore/jsgi/transactional").Transactional(
// Handle sessions
require("./jsgi/session").Session({},
// Do authentication
require("./jsgi/auth").Authentication(config.security,
// Handle request conneg, converting from byte representations to JS objects
require("./jsgi/media").Deserialize(config.mediaSelector,
// Non-REST custom handlers
require('./jsgi/routes').Routes(config.customRoutes,
// Add and retrieve metadata from objects
exports.directApp = require("./jsgi/metadata").Metadata(
// Final REST handler
require("./jsgi/rest-store").RestStore(config)
)
)
)
)
)
)
)
)
)
)
)
)
)
)
);
};
exports.JsgiApp = JsgiApp;
var Connector = require("tunguska/connector").Connector;
{module: './context', config: {}},
// We detect if the request could have been forged from another site
'./csrf',
// Support handling various cross-site request mechanisms like JSONP, window.name, CS-XHR
'./xsite',
// Handle header emulation through query parameters (useful for cross-site and links)
'./http-params',
// Handle HEAD requests
'./head',
// Add some useful headers
{module: './pintura-headers', config: config.serverName},
// Handle conditional requests
{module: './conditional', config: true},
// Handle response conneg, converting from JS objects to byte representations
{factory: require('./jsgi/media').Serialize, config: config.mediaSelector},
// Handle errors that are thrown, converting to appropriate status codes
'./error',
// Handle transactions
'perstore/jsgi/transactional',
// Handle sessions
{module: './session', config: {}},
// Do authentication
{module: './auth', config: config.security},
// Determine access to data models
{module: './access', config: config.security},
// Handle request conneg, converting from byte representations to JS objects
{factory: require('./jsgi/media').Deserialize, config: config.mediaSelector},
// Non-REST custom handlers
{module: './routes', config: config.customRoutes},
// Add and retrieve metadata from objects
'./metadata',
// Final REST handler
{module: './rest-store', config: config}
]);
var Connector = require('tunguska/connector').Connector;
exports.addConnection = exports.app.addConnection = function(connection){
Connector("local-workers", connection);
connection[connection.on ? "on" : "observe"]("message", function(message){
Connector('local-workers', connection);
connection[connection.on ? 'on' : 'observe']('message', function(message){
message.pathInfo = message.channel || message.to;
if(message.method && message.method !== "subscribe"){
if(message.method && message.method !== 'subscribe'){
exports.directApp(message);

@@ -92,0 +92,0 @@ }

@@ -74,11 +74,10 @@ [Pintura](http://www.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=pintura&sll=40.554798,-111.881839&sspn=0.009211,0.016351&ie=UTF8&hq=&hnear=Pintura,+Washington,+Utah&ll=37.31666,-113.171539&spn=0.308538,0.523224&t=p&z=11)

We can then expose this data model through Pintura's HTTP REST interface by implementing
the getDataModel function on the pintura module. This function is called for each HTTP
request:
We can then expose this data model through Pintura's HTTP REST by registering our
models through the `pintura/jsgi/access` module. The module description contains
more information on registering modules for different groups and users,
but we can easily just register our model for use for everyone:
require("pintura/pintura").getDataModel = function(request){
return {
Product: Product
};
};
require("pintura/jsgi/access").registerModels({
Product: Product
});

@@ -136,3 +135,4 @@ Our data model will then be available at the path of /Product/ such that we can make

and which facets or access levels each user is given. The security configuration object
is available at require("pintura/pintura").config.security. The primary functions
is available at require("pintura/pintura").config.security, and can be configured
by calling the `configure` method with object properties to be assigned. The primary functions
that can be overriden or used are:

@@ -155,43 +155,88 @@

require("pintura/pintura").config.security.encryptPassword = function(username, password){
return password;
};
require("pintura/pintura").configure({
security: {
encryptPassword: function(username, password){
return password;
}
}
});
## Access After Authentication
Once authentication is established, we could then use the user's authentication state to restrict or allow access to different
parts of the application data model. For example, we could check to see if a user is
logged to determine if we should provide access to the "Secret" data:
Once authentication is established, access to the data is determined by group membership.
We can define group membership on the security object as well. The security object
has several methods that are used to compute the groups for a user, and which models
to expose based on these groups.
var publicModel = {
Product: Product
};
var authorizedModel = {
Product: Product,
Secret: SecretModel
};
require("pintura/pintura").getDataModel = function(request){
var user = request.remoteUser;
if(user){
return authorizedModel;
The simplest way to define access to models is to define the groups for users on the
security object's `groupsUsers`. We can do this be assigning groups to `groupsUsers`
object by properties with the group name and a value of an array of the included users:
var pintura = require("pintura/pintura");
pintura.configure({
security: {
groupsUsers: {
// define john to be the only admin
admin: ['john'],
// define all users to be in the common group
common: ['*'],
// define unauthenticated users to be in the public group
public: [null]
}
}
return publicModel;
};
});
We could then register our data models with group information associated with it. We
use the registerModels function to accomplish this. This can defined with an object where
properties define the model by name, where each property value can be a model, a model
with the groups allowed, or an array of models with groups. This is best illustrated
by an example:
pintura.registerModels({
// The User model is exposed (though /User/), regardless of which user/group
User: User,
// Expose the Product model, dependent on the group
Product: [
{
// The main (unrestricted) Product model, for anyone in the admin group
model: Product,
groups: ['admin']
},
{
// The public (restricted) Product facet, for those in the user or public group
model: PublicProduct,
groups: ['user', 'public']
}
],
}, {
// define a set of models to be exposed for the admin groups
groups: ['admin'],
models: {
// we could define multiple models (that are available to the admin),
// but here we are just defining the File model to be exposed.
File: File
}
});
We could also potentially have a data model that is readonly for some users and
editable for others. In the example above, we could specify that the Product table
is readonly for users that are not logged in:
editable for others. In the example above, we had specified Product table
to be readonly for users that do not have admin access, we can create this read-only
facet using the Restrictive facet constructor:
var Restrictive = require("perstore/facet").Restrictive;
var publicModel = {
// the Product table is restricted to readonly for public access
Product: Restrictive(Product)
};
var authorizedModel = {
// the Product table is unrestricted here for authorized users
Product: Product,
Secret: SecretModel
};
// assign the data model based on authentication as above
var PublicProduct = Restrictive(Product);
The security object also includes the following methods and properties that can be overriden or used to
provide customized determination of group membership or access to data models:
* groupsUsers - This is the object that defines the users in each group, as described above.
* getGroupsForUser(user) - This should return an array of groups, for which the user
has membership.
* getModelForUser(user) - This should return the specific set of data models for the given user.
This can be overriden to define a custom method for determining the data model.
In addition, you can also alternately define a `getDataModel(request)` method on the pintura
module object, to determine the data model.
Error Handling

@@ -473,4 +518,8 @@ ===========

described below. However, understanding the middleware modules can be important
in understanding the full capabilities of Pintura.
in understanding the full capabilities of Pintura, and for reconfiguring the middleware.
Once you have defined a JSGI app, you can start your server with that app like:
require("pintura/start-node").start(app);
The middleware modules in Pintura

@@ -481,5 +530,49 @@ are found in the "jsgi" folder. Most of these modules directly a function that can be

## configure
The Pintura middleware stack is defined by using the `configure` module. This is a function
that takes an array of middleware definitions and creates the stack for you. Once the middleware
stack has been created, you can also modify the stack, using standard array methods. For example,
to create a stack of middleware, we could write:
require('pintura/jsgi/configure');
configuredApp = configure([
{ // we can define middleware by referencing the module
module: 'pintura/jsgi/auth',
// and providing a config
config: security
},
'pintura/jsgi/csrf' // or just use a module id directly
]);
Each middleware entry in the stack can be defined as one of these:
* An object with a module id in the `module` property, or a factory function in the `factory` property. You can optionally include a `config` property if the middleware takes a config in its arguments.
* A plain module id.
* A factory function.
The return app will then have standard array methods available for modifying the stack. Any changes to the stack will cause it automatically rebuild itself. In addition, there are several extra methods:
* get(id) - Get the configuration for a middleware by the module's last segment (in the case of 'pintura/jsgi/csrf', the id would 'csrf'), or the function name.
* set(id, config) - Update the configuration of one of the middleware.
* delete(id) - Remove a middleware component from the stack.
The `indexOf` and `lastIndexOf` methods also support a string id as the argument, in which case it will search for a config with that id.
## access
{
module: 'pintura/jsgi/access',
config: security
}
The access module provides access to the data models, based on the group membership of the currently
authenticated user. The group membership is typically defined on the security object, which is
then used by access module to calculate the data models available for that user.
## auth
app = require('pintura/jsgi/auth')(security, nextApp);
{
module: 'pintura/jsgi/auth',
config: security
}

@@ -495,3 +588,6 @@ The auth module handles HTTP authorization, performing the HTTP request side of user

app = require('pintura/jsgi/rest-store')(config);
{
module: 'pintura/jsgi/rest-store',
config: config
}

@@ -498,0 +594,0 @@ This module delegates the HTTP REST requests to the appropriate data model. This

@@ -11,3 +11,4 @@ /**

sha1 = require("./util/sha1").b64_sha1,
settings = require("perstore/util/settings");
settings = require("perstore/util/settings"),
modelModule = require("perstore/model");

@@ -53,3 +54,5 @@ try{

var admins = settings.security && settings.security.admins;
var groupToModels = {};
var security = {
// authentication methods:
encryptPassword: function(username, password){

@@ -118,2 +121,81 @@ return password && sha1(password);

userModel = value;
},
// access methods:
groupsUsers: {
user: ['*'],
public: [null]
},
getGroupsForUser: function(user){
var groupsUsers = this.groupsUsers;
var groupsForUser = [];
// at some point we may want to convert this to an array for faster access
for(var groupName in groupsUsers){
var users = groupsUsers[groupName];
if(users.indexOf(user) > - 1 || users.indexOf('*') > - 1){
// the user is in this group
groupsForUser.push(groupName);
}
}
return groupsForUser;
},
modelForUsers: {},
getModelForUser: function(user){
if(this.modelForUsers[user]){
// check the cache
return this.modelForUsers[user];
}
var groups = this.getGroupsForUser(user).concat(['_default']);
var model = {};
groups.forEach(function(group){
var groupModel = groupToModels[group];
for(var key in groupModel){
if(!model[key] || !(model[key].quality > groupModel[key])){
model[key] = groupModel[key];
}
}
});
this.modelForUsers[user] = model;
modelModule.initializeRoot(model);
return model;
},
registerModels: function(){
var groups;
processItem([].slice.call(arguments));
function processItem(item, key){
if(item instanceof Array){
// process each item in an array
item.forEach(function(item){
processItem(item, key);
});
}else if(item && item.groups && typeof item === 'object'){
// define the groups and recurse
groups = item.groups;
if(item.model){
if(typeof item.model !== 'function'){
throw new Error('Model in ' + key + ' does not appear to be a valid model constructor' + item.model);
}
}else if(!item.models){
throw new Error('No valid model provided for ' + key + ' with groups ' + groups);
}
processItem(item.models || item.model, key);
groups = null;
}else if(typeof item === 'function'){
// a model itself, add the definition
if(!key){
throw new Error('No key defined for model');
}
(groups || item.groups || ['_default']).forEach(function(group){
(groupToModels[group] || (groupToModels[group] = {}))[key] = item;
});
}else if(item && typeof item === 'object'){
// an object hash, process each as a key
for(key in item){
processItem(item[key], key);
}
}else{
throw new Error('An invalid value encountered in model registration ' + item + ' for groups ' + groups + ' for name ' + key);
}
}
}

@@ -120,0 +202,0 @@ };

// helpful for debugging
var settings = require("perstore/util/settings"),
ws = require("websocket-server"),
ws = require("ws"),
messageJson = require("./media/message/json");

@@ -12,5 +12,3 @@

server.listen(port);
require("jsgi-node/ws-jsgi")(ws.createServer({
server: server
}), function(request){
require("jsgi-node/ws-jsgi")(ws.createServer({ server: server }), function(request){
request.method = "POST";

@@ -23,5 +21,5 @@ var headers = request.headers;

});
console.log("Listening on port " + port);
return server;
};

@@ -1,2 +0,2 @@

exports.testFullRest = require("./rest");
exports.testConfigure = require("./jsgi/configure");
exports.testJSGIMiddleware = require("./jsgi/index");

@@ -3,0 +3,0 @@

exports.testCSRF = require("./csrf");
exports.testConfigure = require("./csrf");

@@ -3,0 +4,0 @@ if (require.main === module)

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc