Skip to content

Commit 7f5d744

Browse files
committed
Initial release, parse-server, 2.0.0
0 parents  commit 7f5d744

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+14974
-0
lines changed

.gitignore

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Logs
2+
logs
3+
*.log
4+
5+
# Runtime data
6+
pids
7+
*.pid
8+
*.seed
9+
10+
# Directory for instrumented libs generated by jscoverage/JSCover
11+
lib-cov
12+
13+
# Coverage directory used by tools like istanbul
14+
coverage
15+
16+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17+
.grunt
18+
19+
# node-waf configuration
20+
.lock-wscript
21+
22+
# Compiled binary addons (http://nodejs.org/api/addons.html)
23+
build/Release
24+
25+
# Dependency directory
26+
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27+
node_modules
28+
29+
# Emacs
30+
*~

Auth.js

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
var deepcopy = require('deepcopy');
2+
var Parse = require('parse/node').Parse;
3+
var RestQuery = require('./RestQuery');
4+
5+
var cache = require('./cache');
6+
7+
// An Auth object tells you who is requesting something and whether
8+
// the master key was used.
9+
// userObject is a Parse.User and can be null if there's no user.
10+
function Auth(config, isMaster, userObject) {
11+
this.config = config;
12+
this.isMaster = isMaster;
13+
this.user = userObject;
14+
15+
// Assuming a users roles won't change during a single request, we'll
16+
// only load them once.
17+
this.userRoles = [];
18+
this.fetchedRoles = false;
19+
this.rolePromise = null;
20+
}
21+
22+
// Whether this auth could possibly modify the given user id.
23+
// It still could be forbidden via ACLs even if this returns true.
24+
Auth.prototype.couldUpdateUserId = function(userId) {
25+
if (this.isMaster) {
26+
return true;
27+
}
28+
if (this.user && this.user.id === userId) {
29+
return true;
30+
}
31+
return false;
32+
};
33+
34+
// A helper to get a master-level Auth object
35+
function master(config) {
36+
return new Auth(config, true, null);
37+
}
38+
39+
// A helper to get a nobody-level Auth object
40+
function nobody(config) {
41+
return new Auth(config, false, null);
42+
}
43+
44+
// Returns a promise that resolves to an Auth object
45+
var getAuthForSessionToken = function(config, sessionToken) {
46+
var cachedUser = cache.getUser(sessionToken);
47+
if (cachedUser) {
48+
return Promise.resolve(new Auth(config, false, cachedUser));
49+
}
50+
var restOptions = {
51+
limit: 1,
52+
include: 'user'
53+
};
54+
var restWhere = {
55+
_session_token: sessionToken
56+
};
57+
var query = new RestQuery(config, master(config), '_Session',
58+
restWhere, restOptions);
59+
return query.execute().then((response) => {
60+
var results = response.results;
61+
if (results.length !== 1 || !results[0]['user']) {
62+
return nobody(config);
63+
}
64+
var obj = results[0]['user'];
65+
delete obj.password;
66+
obj['className'] = '_User';
67+
var userObject = Parse.Object.fromJSON(obj);
68+
cache.setUser(sessionToken, userObject);
69+
return new Auth(config, false, userObject);
70+
});
71+
};
72+
73+
// Returns a promise that resolves to an array of role names
74+
Auth.prototype.getUserRoles = function() {
75+
if (this.isMaster || !this.user) {
76+
return Promise.resolve([]);
77+
}
78+
if (this.fetchedRoles) {
79+
return Promise.resolve(this.userRoles);
80+
}
81+
if (this.rolePromise) {
82+
return rolePromise;
83+
}
84+
this.rolePromise = this._loadRoles();
85+
return this.rolePromise;
86+
};
87+
88+
// Iterates through the role tree and compiles a users roles
89+
Auth.prototype._loadRoles = function() {
90+
var restWhere = {
91+
'users': {
92+
__type: 'Pointer',
93+
className: '_User',
94+
objectId: this.user.id
95+
}
96+
};
97+
// First get the role ids this user is directly a member of
98+
var query = new RestQuery(this.config, master(this.config), '_Role',
99+
restWhere, {});
100+
return query.execute().then((response) => {
101+
var results = response.results;
102+
if (!results.length) {
103+
this.userRoles = [];
104+
this.fetchedRoles = true;
105+
this.rolePromise = null;
106+
return Promise.resolve(this.userRoles);
107+
}
108+
109+
var roleIDs = results.map(r => r.objectId);
110+
var promises = [Promise.resolve(roleIDs)];
111+
for (var role of roleIDs) {
112+
promises.push(this._getAllRoleNamesForId(role));
113+
}
114+
return Promise.all(promises).then((results) => {
115+
var allIDs = [];
116+
for (var x of results) {
117+
Array.prototype.push.apply(allIDs, x);
118+
}
119+
var restWhere = {
120+
objectId: {
121+
'$in': allIDs
122+
}
123+
};
124+
var query = new RestQuery(this.config, master(this.config),
125+
'_Role', restWhere, {});
126+
return query.execute();
127+
}).then((response) => {
128+
var results = response.results;
129+
this.userRoles = results.map((r) => {
130+
return 'role:' + r.name;
131+
});
132+
this.fetchedRoles = true;
133+
this.rolePromise = null;
134+
return Promise.resolve(this.userRoles);
135+
});
136+
});
137+
};
138+
139+
// Given a role object id, get any other roles it is part of
140+
// TODO: Make recursive to support role nesting beyond 1 level deep
141+
Auth.prototype._getAllRoleNamesForId = function(roleID) {
142+
var rolePointer = {
143+
__type: 'Pointer',
144+
className: '_Role',
145+
objectId: roleID
146+
};
147+
var restWhere = {
148+
'$relatedTo': {
149+
key: 'roles',
150+
object: rolePointer
151+
}
152+
};
153+
var query = new RestQuery(this.config, master(this.config), '_Role',
154+
restWhere, {});
155+
return query.execute().then((response) => {
156+
var results = response.results;
157+
if (!results.length) {
158+
return Promise.resolve([]);
159+
}
160+
var roleIDs = results.map(r => r.objectId);
161+
return Promise.resolve(roleIDs);
162+
});
163+
};
164+
165+
module.exports = {
166+
Auth: Auth,
167+
master: master,
168+
nobody: nobody,
169+
getAuthForSessionToken: getAuthForSessionToken
170+
};

Config.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// A Config object provides information about how a specific app is
2+
// configured.
3+
// mount is the URL for the root of the API; includes http, domain, etc.
4+
function Config(applicationId, mount) {
5+
var cache = require('./cache');
6+
var DatabaseAdapter = require('./DatabaseAdapter');
7+
8+
var cacheInfo = cache.apps[applicationId];
9+
this.valid = !!cacheInfo;
10+
if (!this.valid) {
11+
return;
12+
}
13+
14+
this.applicationId = applicationId;
15+
this.collectionPrefix = cacheInfo.collectionPrefix || '';
16+
this.database = DatabaseAdapter.getDatabaseConnection(applicationId);
17+
this.masterKey = cacheInfo.masterKey;
18+
this.clientKey = cacheInfo.clientKey;
19+
this.javascriptKey = cacheInfo.javascriptKey;
20+
this.dotNetKey = cacheInfo.dotNetKey;
21+
this.restAPIKey = cacheInfo.restAPIKey;
22+
this.fileKey = cacheInfo.fileKey;
23+
this.mount = mount;
24+
}
25+
26+
27+
module.exports = Config;

DatabaseAdapter.js

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Database Adapter
2+
//
3+
// Allows you to change the underlying database.
4+
//
5+
// Adapter classes must implement the following methods:
6+
// * a constructor with signature (connectionString, optionsObject)
7+
// * connect()
8+
// * loadSchema()
9+
// * create(className, object)
10+
// * find(className, query, options)
11+
// * update(className, query, update, options)
12+
// * destroy(className, query, options)
13+
// * This list is incomplete and the database process is not fully modularized.
14+
//
15+
// Default is ExportAdapter, which uses mongo.
16+
17+
var ExportAdapter = require('./ExportAdapter');
18+
19+
var adapter = ExportAdapter;
20+
var cache = require('./cache');
21+
var dbConnections = {};
22+
var databaseURI = 'mongodb://localhost:27017/parse';
23+
24+
function setAdapter(databaseAdapter) {
25+
adapter = databaseAdapter;
26+
}
27+
28+
function setDatabaseURI(uri) {
29+
databaseURI = uri;
30+
}
31+
32+
function getDatabaseConnection(appId) {
33+
if (dbConnections[appId]) {
34+
return dbConnections[appId];
35+
}
36+
dbConnections[appId] = new adapter(databaseURI, {
37+
collectionPrefix: cache.apps[appId]['collectionPrefix']
38+
});
39+
dbConnections[appId].connect();
40+
return dbConnections[appId];
41+
}
42+
43+
module.exports = {
44+
dbConnections: dbConnections,
45+
getDatabaseConnection: getDatabaseConnection,
46+
setAdapter: setAdapter,
47+
setDatabaseURI: setDatabaseURI
48+
};

0 commit comments

Comments
 (0)