Skip to content

Commit

Permalink
Initial release, parse-server, 2.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
gfosco committed Jan 28, 2016
0 parents commit 7f5d744
Show file tree
Hide file tree
Showing 53 changed files with 14,974 additions and 0 deletions.
30 changes: 30 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules

# Emacs
*~
170 changes: 170 additions & 0 deletions Auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
var deepcopy = require('deepcopy');
var Parse = require('parse/node').Parse;
var RestQuery = require('./RestQuery');

var cache = require('./cache');

// An Auth object tells you who is requesting something and whether
// the master key was used.
// userObject is a Parse.User and can be null if there's no user.
function Auth(config, isMaster, userObject) {
this.config = config;
this.isMaster = isMaster;
this.user = userObject;

// Assuming a users roles won't change during a single request, we'll
// only load them once.
this.userRoles = [];
this.fetchedRoles = false;
this.rolePromise = null;
}

// Whether this auth could possibly modify the given user id.
// It still could be forbidden via ACLs even if this returns true.
Auth.prototype.couldUpdateUserId = function(userId) {
if (this.isMaster) {
return true;
}
if (this.user && this.user.id === userId) {
return true;
}
return false;
};

// A helper to get a master-level Auth object
function master(config) {
return new Auth(config, true, null);
}

// A helper to get a nobody-level Auth object
function nobody(config) {
return new Auth(config, false, null);
}

// Returns a promise that resolves to an Auth object
var getAuthForSessionToken = function(config, sessionToken) {
var cachedUser = cache.getUser(sessionToken);
if (cachedUser) {
return Promise.resolve(new Auth(config, false, cachedUser));
}
var restOptions = {
limit: 1,
include: 'user'
};
var restWhere = {
_session_token: sessionToken
};
var query = new RestQuery(config, master(config), '_Session',
restWhere, restOptions);
return query.execute().then((response) => {
var results = response.results;
if (results.length !== 1 || !results[0]['user']) {
return nobody(config);
}
var obj = results[0]['user'];
delete obj.password;
obj['className'] = '_User';
var userObject = Parse.Object.fromJSON(obj);
cache.setUser(sessionToken, userObject);
return new Auth(config, false, userObject);
});
};

// Returns a promise that resolves to an array of role names
Auth.prototype.getUserRoles = function() {
if (this.isMaster || !this.user) {
return Promise.resolve([]);
}
if (this.fetchedRoles) {
return Promise.resolve(this.userRoles);
}
if (this.rolePromise) {
return rolePromise;
}
this.rolePromise = this._loadRoles();
return this.rolePromise;
};

// Iterates through the role tree and compiles a users roles
Auth.prototype._loadRoles = function() {
var restWhere = {
'users': {
__type: 'Pointer',
className: '_User',
objectId: this.user.id
}
};
// First get the role ids this user is directly a member of
var query = new RestQuery(this.config, master(this.config), '_Role',
restWhere, {});
return query.execute().then((response) => {
var results = response.results;
if (!results.length) {
this.userRoles = [];
this.fetchedRoles = true;
this.rolePromise = null;
return Promise.resolve(this.userRoles);
}

var roleIDs = results.map(r => r.objectId);
var promises = [Promise.resolve(roleIDs)];
for (var role of roleIDs) {
promises.push(this._getAllRoleNamesForId(role));
}
return Promise.all(promises).then((results) => {
var allIDs = [];
for (var x of results) {
Array.prototype.push.apply(allIDs, x);
}
var restWhere = {
objectId: {
'$in': allIDs
}
};
var query = new RestQuery(this.config, master(this.config),
'_Role', restWhere, {});
return query.execute();
}).then((response) => {
var results = response.results;
this.userRoles = results.map((r) => {
return 'role:' + r.name;
});
this.fetchedRoles = true;
this.rolePromise = null;
return Promise.resolve(this.userRoles);
});
});
};

// Given a role object id, get any other roles it is part of
// TODO: Make recursive to support role nesting beyond 1 level deep
Auth.prototype._getAllRoleNamesForId = function(roleID) {
var rolePointer = {
__type: 'Pointer',
className: '_Role',
objectId: roleID
};
var restWhere = {
'$relatedTo': {
key: 'roles',
object: rolePointer
}
};
var query = new RestQuery(this.config, master(this.config), '_Role',
restWhere, {});
return query.execute().then((response) => {
var results = response.results;
if (!results.length) {
return Promise.resolve([]);
}
var roleIDs = results.map(r => r.objectId);
return Promise.resolve(roleIDs);
});
};

module.exports = {
Auth: Auth,
master: master,
nobody: nobody,
getAuthForSessionToken: getAuthForSessionToken
};
27 changes: 27 additions & 0 deletions Config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// A Config object provides information about how a specific app is
// configured.
// mount is the URL for the root of the API; includes http, domain, etc.
function Config(applicationId, mount) {
var cache = require('./cache');
var DatabaseAdapter = require('./DatabaseAdapter');

var cacheInfo = cache.apps[applicationId];
this.valid = !!cacheInfo;
if (!this.valid) {
return;
}

this.applicationId = applicationId;
this.collectionPrefix = cacheInfo.collectionPrefix || '';
this.database = DatabaseAdapter.getDatabaseConnection(applicationId);
this.masterKey = cacheInfo.masterKey;
this.clientKey = cacheInfo.clientKey;
this.javascriptKey = cacheInfo.javascriptKey;
this.dotNetKey = cacheInfo.dotNetKey;
this.restAPIKey = cacheInfo.restAPIKey;
this.fileKey = cacheInfo.fileKey;
this.mount = mount;
}


module.exports = Config;
48 changes: 48 additions & 0 deletions DatabaseAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Database Adapter
//
// Allows you to change the underlying database.
//
// Adapter classes must implement the following methods:
// * a constructor with signature (connectionString, optionsObject)
// * connect()
// * loadSchema()
// * create(className, object)
// * find(className, query, options)
// * update(className, query, update, options)
// * destroy(className, query, options)
// * This list is incomplete and the database process is not fully modularized.
//
// Default is ExportAdapter, which uses mongo.

var ExportAdapter = require('./ExportAdapter');

var adapter = ExportAdapter;
var cache = require('./cache');
var dbConnections = {};
var databaseURI = 'mongodb://localhost:27017/parse';

function setAdapter(databaseAdapter) {
adapter = databaseAdapter;
}

function setDatabaseURI(uri) {
databaseURI = uri;
}

function getDatabaseConnection(appId) {
if (dbConnections[appId]) {
return dbConnections[appId];
}
dbConnections[appId] = new adapter(databaseURI, {
collectionPrefix: cache.apps[appId]['collectionPrefix']
});
dbConnections[appId].connect();
return dbConnections[appId];
}

module.exports = {
dbConnections: dbConnections,
getDatabaseConnection: getDatabaseConnection,
setAdapter: setAdapter,
setDatabaseURI: setDatabaseURI
};
Loading

0 comments on commit 7f5d744

Please sign in to comment.