Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix: session validation fails because the default session schema expects date not iso string #135

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c13b61f
Use CSPRNG to generate objectIds
dchest Jan 30, 2016
0330245
Converts httpRequest body to string if needed
natanrolnik Jan 31, 2016
1328270
Fix #119: Empty find options for GET requests
ks-simakov Feb 1, 2016
234c405
Fix #119: remove 'GET' condition and replace it with assign
ks-simakov Feb 1, 2016
e8e7f4b
fix untransform null objects
Feb 1, 2016
956fd08
remove trailing spaces
Feb 1, 2016
07b0235
installationId only gets set on the session if passed in to the request
Feb 1, 2016
26dfdd8
Correctly passes the ParseDate rather than iso string when creating a…
Feb 1, 2016
251ec4d
Merge pull request #73 from dchest/csprng
gfosco Feb 1, 2016
72a956a
Merge pull request #120 from GigabyteTheOne/master
gfosco Feb 1, 2016
952ccd4
Merge pull request #91 from natanrolnik/master
gfosco Feb 1, 2016
9eb4e4c
Merge pull request #121 from JordanVincent/master
gfosco Feb 1, 2016
18b5a30
Merge pull request #129 from spekulatius/trailing-spaces
gfosco Feb 1, 2016
6051e3a
Merge pull request #133 from maysale01/hotfix-session-validation-fail…
gfosco Feb 1, 2016
1cddbb0
:checkered_flag: Add Windows Support (bcrypt > bcrypt-node)
felixrieseberg Feb 1, 2016
7f01a67
Merge pull request #108 from felixrieseberg/bcrypt-node
gfosco Feb 1, 2016
40bfc4a
Added multi-app support in DatabaseAdapter.js
Feb 1, 2016
cb1079a
Addressed bugs with bcrypt-nodejs and changed crypto.js to password.js
gfosco Feb 1, 2016
5216c93
Merge pull request #141 from ParsePlatform/fosco.crypto
gfosco Feb 1, 2016
5dc66fd
Merge pull request #139 from jamiechapman/master
gfosco Feb 1, 2016
86e4297
Some updates to fix tests
gfosco Feb 1, 2016
3f21244
Adding CONTRIBUTING.md and Open Code of Conduct.
gfosco Feb 1, 2016
a814655
Merge pull request #142 from ParsePlatform/fosco.coc
gfosco Feb 1, 2016
05c2246
Add support for saving files to AWS S3
skinp Feb 1, 2016
2008c4d
Use ES6 => instead of self = this ack
skinp Feb 1, 2016
3191793
Merge pull request #113 from skinp/s3-files
gfosco Feb 1, 2016
00540cb
Correctly passes the ParseDate rather than iso string when creating a…
Feb 1, 2016
3063013
rebasing
Feb 1, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
### Contributing to Parse Server

#### Pull Requests Welcome!

We really want Parse to be yours, to see it grow and thrive in the open source community.

##### Please Do's

* Please write tests to cover new methods.
* Please run the tests and make sure you didn't break anything.

##### Code of Conduct

This project adheres to the [Open Code of Conduct][code-of-conduct]. By participating, you are expected to honor this code.
[code-of-conduct]: http://todogroup.org/opencodeofconduct/#Parse Server/fjm@fb.com


12 changes: 10 additions & 2 deletions DatabaseAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ var adapter = ExportAdapter;
var cache = require('./cache');
var dbConnections = {};
var databaseURI = 'mongodb://localhost:27017/parse';
var appDatabaseURIs = {};

function setAdapter(databaseAdapter) {
adapter = databaseAdapter;
Expand All @@ -29,11 +30,17 @@ function setDatabaseURI(uri) {
databaseURI = uri;
}

function setAppDatabaseURI(appId, uri) {
appDatabaseURIs[appId] = uri;
}

function getDatabaseConnection(appId) {
if (dbConnections[appId]) {
return dbConnections[appId];
}
dbConnections[appId] = new adapter(databaseURI, {

var dbURI = (appDatabaseURIs[appId] ? appDatabaseURIs[appId] : databaseURI);
dbConnections[appId] = new adapter(dbURI, {
collectionPrefix: cache.apps[appId]['collectionPrefix']
});
dbConnections[appId].connect();
Expand All @@ -44,5 +51,6 @@ module.exports = {
dbConnections: dbConnections,
getDatabaseConnection: getDatabaseConnection,
setAdapter: setAdapter,
setDatabaseURI: setDatabaseURI
setDatabaseURI: setDatabaseURI,
setAppDatabaseURI: setAppDatabaseURI
};
1 change: 1 addition & 0 deletions FilesAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// Adapter classes must implement the following functions:
// * create(config, filename, data)
// * get(config, filename)
// * location(config, req, filename)
//
// Default is GridStoreAdapter, which requires mongo
// and for the API server to be using the ExportAdapter
Expand Down
12 changes: 11 additions & 1 deletion GridStoreAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// Requires the database adapter to be based on mongoclient

var GridStore = require('mongodb').GridStore;
var path = require('path');

// For a given config object, filename, and data, store a file
// Returns a promise
Expand Down Expand Up @@ -32,7 +33,16 @@ function get(config, filename) {
});
}

// Generates and returns the location of a file stored in GridStore for the
// given request and filename
function location(config, req, filename) {
return (req.protocol + '://' + req.get('host') +
path.dirname(req.originalUrl) + '/' + req.config.applicationId +
'/' + encodeURIComponent(filename));
}

module.exports = {
create: create,
get: get
get: get,
location: location
};
16 changes: 10 additions & 6 deletions RestWrite.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
// that writes to the database.
// This could be either a "create" or an "update".

var crypto = require('crypto');
var deepcopy = require('deepcopy');
var rack = require('hat').rack();

var Auth = require('./Auth');
var cache = require('./cache');
var Config = require('./Config');
var crypto = require('./crypto');
var passwordCrypto = require('./password');
var facebook = require('./facebook');
var Parse = require('parse/node');
var triggers = require('./triggers');
Expand Down Expand Up @@ -299,7 +300,7 @@ RestWrite.prototype.transformUser = function() {
if (this.query) {
this.storage['clearSessions'] = true;
}
return crypto.hash(this.data.password).then((hashedPassword) => {
return passwordCrypto.hash(this.data.password).then((hashedPassword) => {
this.data._hashed_password = hashedPassword;
delete this.data.password;
});
Expand Down Expand Up @@ -701,15 +702,18 @@ RestWrite.prototype.objectId = function() {
return this.data.objectId || this.query.objectId;
};

// Returns a string that's usable as an object id.
// Probably unique. Good enough? Probably!
// Returns a unique string that's usable as an object id.
function newObjectId() {
var chars = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
'abcdefghijklmnopqrstuvwxyz' +
'0123456789');
var objectId = '';
for (var i = 0; i < 10; ++i) {
objectId += chars[Math.floor(Math.random() * chars.length)];
var bytes = crypto.randomBytes(10);
for (var i = 0; i < bytes.length; ++i) {
// Note: there is a slight modulo bias, because chars length
// of 62 doesn't divide the number of all bytes (256) evenly.
// It is acceptable for our purposes.
objectId += chars[bytes.readUInt8(i) % chars.length];
}
return objectId;
}
Expand Down
77 changes: 77 additions & 0 deletions S3Adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// S3Adapter
//
// Stores Parse files in AWS S3.

var AWS = require('aws-sdk');
var path = require('path');

var DEFAULT_REGION = "us-east-1";
var DEFAULT_BUCKET = "parse-files";

// Creates an S3 session.
// Providing AWS access and secret keys is mandatory
// Region and bucket will use sane defaults if omitted
function S3Adapter(accessKey, secretKey, options) {
options = options || {};

this.region = options.region || DEFAULT_REGION;
this.bucket = options.bucket || DEFAULT_BUCKET;
this.bucketPrefix = options.bucketPrefix || "";
this.directAccess = options.directAccess || false;

s3Options = {
accessKeyId: accessKey,
secretAccessKey: secretKey,
params: {Bucket: this.bucket}
};
AWS.config.region = this.region;
this.s3 = new AWS.S3(s3Options);
}

// For a given config object, filename, and data, store a file in S3
// Returns a promise containing the S3 object creation response
S3Adapter.prototype.create = function(config, filename, data) {
var params = {
Key: this.bucketPrefix + filename,
Body: data,
};
if (this.directAccess) {
params.ACL = "public-read"
}

return new Promise((resolve, reject) => {
this.s3.upload(params, function(err, data) {
if (err !== null) return reject(err);
resolve(data);
});
});
}

// Search for and return a file if found by filename
// Returns a promise that succeeds with the buffer result from S3
S3Adapter.prototype.get = function(config, filename) {
var params = {Key: this.bucketPrefix + filename};

return new Promise((resolve, reject) => {
this.s3.getObject(params, (err, data) => {
if (err !== null) return reject(err);
resolve(data.Body);
});
});
}

// Generates and returns the location of a file stored in S3 for the given request and
// filename
// The location is the direct S3 link if the option is set, otherwise we serve
// the file through parse-server
S3Adapter.prototype.location = function(config, req, filename) {
if (this.directAccess) {
return ('https://' + this.bucket + '.s3.amazonaws.com' + '/' +
this.bucketPrefix + filename);
}
return (req.protocol + '://' + req.get('host') +
path.dirname(req.originalUrl) + '/' + req.config.applicationId +
'/' + encodeURIComponent(filename));
}

module.exports = S3Adapter;
33 changes: 17 additions & 16 deletions classes.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,36 @@ var router = new PromiseRouter();

// Returns a promise that resolves to a {response} object.
function handleFind(req) {
var body = Object.assign(req.body, req.query);
var options = {};
if (req.body.skip) {
options.skip = Number(req.body.skip);
if (body.skip) {
options.skip = Number(body.skip);
}
if (req.body.limit) {
options.limit = Number(req.body.limit);
if (body.limit) {
options.limit = Number(body.limit);
}
if (req.body.order) {
options.order = String(req.body.order);
if (body.order) {
options.order = String(body.order);
}
if (req.body.count) {
if (body.count) {
options.count = true;
}
if (typeof req.body.keys == 'string') {
options.keys = req.body.keys;
if (typeof body.keys == 'string') {
options.keys = body.keys;
}
if (req.body.include) {
options.include = String(req.body.include);
if (body.include) {
options.include = String(body.include);
}
if (req.body.redirectClassNameForKey) {
options.redirectClassNameForKey = String(req.body.redirectClassNameForKey);
if (body.redirectClassNameForKey) {
options.redirectClassNameForKey = String(body.redirectClassNameForKey);
}

if(typeof req.body.where === 'string') {
req.body.where = JSON.parse(req.body.where);
if(typeof body.where === 'string') {
body.where = JSON.parse(body.where);
}

return rest.find(req.config, req.auth,
req.params.className, req.body.where, options)
req.params.className, body.where, options)
.then((response) => {
return {response: response};
});
Expand Down
6 changes: 1 addition & 5 deletions files.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ var bodyParser = require('body-parser'),
middlewares = require('./middlewares.js'),
mime = require('mime'),
Parse = require('parse/node').Parse,
path = require('path'),
rack = require('hat').rack();

var router = express.Router();
Expand Down Expand Up @@ -44,10 +43,7 @@ var processCreate = function(req, res, next) {
FilesAdapter.getAdapter().create(req.config, filename, req.body)
.then(() => {
res.status(201);
var location = (req.protocol + '://' + req.get('host') +
path.dirname(req.originalUrl) + '/' +
req.config.applicationId + '/' +
encodeURIComponent(filename));
var location = FilesAdapter.getAdapter().location(req.config, req, filename);
res.set('Location', location);
res.json({ url: location, name: filename });
}).catch((error) => {
Expand Down
6 changes: 4 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function ParseServer(args) {
FilesAdapter.setAdapter(args.filesAdapter);
}
if (args.databaseURI) {
DatabaseAdapter.setDatabaseURI(args.databaseURI);
DatabaseAdapter.setAppDatabaseURI(args.appId, args.databaseURI);
}
if (args.cloud) {
addParseCloud();
Expand Down Expand Up @@ -150,6 +150,9 @@ function addParseCloud() {
options.uri = options.url;
delete options.url;
}
if (typeof options.body === 'object') {
options.body = JSON.stringify(options.body);
}
request(options, (error, response, body) => {
if (error) {
if (callbacks.error) {
Expand Down Expand Up @@ -178,4 +181,3 @@ function getClassName(parseClass) {
module.exports = {
ParseServer: ParseServer
};

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
},
"license": "BSD-3-Clause",
"dependencies": {
"bcrypt": "~0.8",
"aws-sdk": "~2.2.33",
"bcrypt-nodejs": "0.0.3",
"body-parser": "~1.12.4",
"deepcopy": "^0.5.0",
"express": "~4.2.x",
Expand Down
4 changes: 2 additions & 2 deletions crypto.js → password.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Tools for encrypting and decrypting passwords.
// Basically promise-friendly wrappers for bcrypt.
var bcrypt = require('bcrypt');
var bcrypt = require('bcrypt-nodejs');

// Returns a promise for a hashed password string.
function hash(password) {
return new Promise(function(fulfill, reject) {
bcrypt.hash(password, 8, function(err, hashedPassword) {
bcrypt.hash(password, null, null, function(err, hashedPassword) {
if (err) {
reject(err);
} else {
Expand Down
6 changes: 3 additions & 3 deletions spec/ParseUser.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// Tests that involve sending password reset emails.

var request = require('request');
var crypto = require('../crypto');
var passwordCrypto = require('../password');

describe('Parse.User testing', () => {
it("user sign up class method", (done) => {
Expand Down Expand Up @@ -1560,7 +1560,7 @@ describe('Parse.User testing', () => {

it('password format matches hosted parse', (done) => {
var hashed = '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie';
crypto.compare('test', hashed)
passwordCrypto.compare('test', hashed)
.then((pass) => {
expect(pass).toBe(true);
done();
Expand All @@ -1574,7 +1574,7 @@ describe('Parse.User testing', () => {
var sessionToken = null;

Parse.Promise.as().then(function() {
return Parse.User.signUp("fosco", "parse");
return Parse.User.signUp("fosco", "parse");
}).then(function(newUser) {
equal(Parse.User.current(), newUser);
sessionToken = newUser.getSessionToken();
Expand Down