Skip to content

Commit e0704b4

Browse files
authored
Adds class level permission requiring authenticated user (#893)
* Adds class level permission requiring authenticated user * Updates to latest schema permissions syntax * fix flaky test * Exclude PG * Rebased and nitted * lints
1 parent 01b05b0 commit e0704b4

File tree

2 files changed

+250
-1
lines changed

2 files changed

+250
-1
lines changed

spec/Schema.spec.js

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,3 +881,230 @@ describe('SchemaController', () => {
881881
});
882882
});
883883
});
884+
885+
describe('Class Level Permissions for requiredAuth', () => {
886+
887+
beforeEach(() => {
888+
config = new Config('test');
889+
});
890+
891+
function createUser() {
892+
let user = new Parse.User();
893+
user.set("username", "hello");
894+
user.set("password", "world");
895+
return user.signUp(null);
896+
}
897+
898+
it('required auth test find', (done) => {
899+
config.database.loadSchema().then((schema) => {
900+
// Just to create a valid class
901+
return schema.validateObject('Stuff', {foo: 'bar'});
902+
}).then((schema) => {
903+
return schema.setPermissions('Stuff', {
904+
'find': {
905+
'requiresAuthentication': true
906+
}
907+
});
908+
}).then(() => {
909+
var query = new Parse.Query('Stuff');
910+
return query.find();
911+
}).then(() => {
912+
fail('Class permissions should have rejected this query.');
913+
done();
914+
}, (e) => {
915+
expect(e.message).toEqual('Permission denied, user needs to be authenticated.');
916+
done();
917+
});
918+
});
919+
920+
it('required auth test find authenticated', (done) => {
921+
config.database.loadSchema().then((schema) => {
922+
// Just to create a valid class
923+
return schema.validateObject('Stuff', {foo: 'bar'});
924+
}).then((schema) => {
925+
return schema.setPermissions('Stuff', {
926+
'find': {
927+
'requiresAuthentication': true
928+
}
929+
});
930+
}).then(() => {
931+
return createUser();
932+
}).then(() => {
933+
var query = new Parse.Query('Stuff');
934+
return query.find();
935+
}).then((results) => {
936+
expect(results.length).toEqual(0);
937+
done();
938+
}, (e) => {
939+
console.error(e);
940+
fail("Should not have failed");
941+
done();
942+
});
943+
});
944+
945+
it('required auth should allow create authenticated', (done) => {
946+
config.database.loadSchema().then((schema) => {
947+
// Just to create a valid class
948+
return schema.validateObject('Stuff', {foo: 'bar'});
949+
}).then((schema) => {
950+
return schema.setPermissions('Stuff', {
951+
'create': {
952+
'requiresAuthentication': true
953+
}
954+
});
955+
}).then(() => {
956+
return createUser();
957+
}).then(() => {
958+
let stuff = new Parse.Object('Stuff');
959+
stuff.set('foo', 'bar');
960+
return stuff.save();
961+
}).then(() => {
962+
done();
963+
}, (e) => {
964+
console.error(e);
965+
fail("Should not have failed");
966+
done();
967+
});
968+
});
969+
970+
it('required auth should reject create when not authenticated', (done) => {
971+
config.database.loadSchema().then((schema) => {
972+
// Just to create a valid class
973+
return schema.validateObject('Stuff', {foo: 'bar'});
974+
}).then((schema) => {
975+
return schema.setPermissions('Stuff', {
976+
'create': {
977+
'requiresAuthentication': true
978+
}
979+
});
980+
}).then(() => {
981+
let stuff = new Parse.Object('Stuff');
982+
stuff.set('foo', 'bar');
983+
return stuff.save();
984+
}).then(() => {
985+
fail('Class permissions should have rejected this query.');
986+
done();
987+
}, (e) => {
988+
expect(e.message).toEqual('Permission denied, user needs to be authenticated.');
989+
done();
990+
});
991+
});
992+
993+
it('required auth test create/get/update/delete authenticated', (done) => {
994+
config.database.loadSchema().then((schema) => {
995+
// Just to create a valid class
996+
return schema.validateObject('Stuff', {foo: 'bar'});
997+
}).then((schema) => {
998+
return schema.setPermissions('Stuff', {
999+
'create': {
1000+
'requiresAuthentication': true
1001+
},
1002+
'get': {
1003+
'requiresAuthentication': true
1004+
},
1005+
'delete': {
1006+
'requiresAuthentication': true
1007+
},
1008+
'update': {
1009+
'requiresAuthentication': true
1010+
}
1011+
});
1012+
}).then(() => {
1013+
return createUser();
1014+
}).then(() => {
1015+
let stuff = new Parse.Object('Stuff');
1016+
stuff.set('foo', 'bar');
1017+
return stuff.save().then(() => {
1018+
let query = new Parse.Query('Stuff');
1019+
return query.get(stuff.id);
1020+
});
1021+
}).then((gotStuff) => {
1022+
return gotStuff.save({'foo': 'baz'}).then(() => {
1023+
return gotStuff.destroy();
1024+
})
1025+
}).then(() => {
1026+
done();
1027+
}, (e) => {
1028+
console.error(e);
1029+
fail("Should not have failed");
1030+
done();
1031+
});
1032+
});
1033+
1034+
it('required auth test create/get/update/delete not authenitcated', (done) => {
1035+
config.database.loadSchema().then((schema) => {
1036+
// Just to create a valid class
1037+
return schema.validateObject('Stuff', {foo: 'bar'});
1038+
}).then((schema) => {
1039+
return schema.setPermissions('Stuff', {
1040+
'get': {
1041+
'requiresAuthentication': true
1042+
},
1043+
'delete': {
1044+
'requiresAuthentication': true
1045+
},
1046+
'update': {
1047+
'requiresAuthentication': true
1048+
},
1049+
'create': {
1050+
'*': true
1051+
}
1052+
});
1053+
}).then(() => {
1054+
let stuff = new Parse.Object('Stuff');
1055+
stuff.set('foo', 'bar');
1056+
return stuff.save().then(() => {
1057+
let query = new Parse.Query('Stuff');
1058+
return query.get(stuff.id);
1059+
});
1060+
}).then(() => {
1061+
fail("Should not succeed!");
1062+
done();
1063+
}, (e) => {
1064+
expect(e.message).toEqual('Permission denied, user needs to be authenticated.');
1065+
done();
1066+
});
1067+
});
1068+
1069+
it('required auth test create/get/update/delete not authenitcated', (done) => {
1070+
config.database.loadSchema().then((schema) => {
1071+
// Just to create a valid class
1072+
return schema.validateObject('Stuff', {foo: 'bar'});
1073+
}).then((schema) => {
1074+
return schema.setPermissions('Stuff', {
1075+
'find': {
1076+
'requiresAuthentication': true
1077+
},
1078+
'delete': {
1079+
'requiresAuthentication': true
1080+
},
1081+
'update': {
1082+
'requiresAuthentication': true
1083+
},
1084+
'create': {
1085+
'*': true
1086+
},
1087+
'get': {
1088+
'*': true
1089+
}
1090+
});
1091+
}).then(() => {
1092+
let stuff = new Parse.Object('Stuff');
1093+
stuff.set('foo', 'bar');
1094+
return stuff.save().then(() => {
1095+
let query = new Parse.Query('Stuff');
1096+
return query.get(stuff.id);
1097+
})
1098+
}).then((result) => {
1099+
expect(result.get('foo')).toEqual('bar');
1100+
let query = new Parse.Query('Stuff');
1101+
return query.find();
1102+
}).then(() => {
1103+
fail("Should not succeed!");
1104+
done();
1105+
}, (e) => {
1106+
expect(e.message).toEqual('Permission denied, user needs to be authenticated.');
1107+
done();
1108+
});
1109+
});
1110+
})

src/Controllers/SchemaController.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ const roleRegex = /^role:.*/;
123123
// * permission
124124
const publicRegex = /^\*$/
125125

126-
const permissionKeyRegex = Object.freeze([userIdRegex, roleRegex, publicRegex]);
126+
const requireAuthenticationRegex = /^requiresAuthentication$/
127+
128+
const permissionKeyRegex = Object.freeze([userIdRegex, roleRegex, publicRegex, requireAuthenticationRegex]);
127129

128130
function verifyPermissionKey(key) {
129131
let result = permissionKeyRegex.reduce((isGood, regEx) => {
@@ -771,6 +773,26 @@ export default class SchemaController {
771773
return true;
772774
}
773775
let classPerms = this.perms[className];
776+
let perms = classPerms[operation];
777+
778+
// If only for authenticated users
779+
// make sure we have an aclGroup
780+
if (perms['requiresAuthentication']) {
781+
// If aclGroup has * (public)
782+
if (!aclGroup || aclGroup.length == 0) {
783+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
784+
'Permission denied, user needs to be authenticated.');
785+
} else if (aclGroup.indexOf('*') > -1 && aclGroup.length == 1) {
786+
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
787+
'Permission denied, user needs to be authenticated.');
788+
}
789+
// no other CLP than requiresAuthentication
790+
// let's resolve that!
791+
if (Object.keys(perms).length == 1) {
792+
return Promise.resolve();
793+
}
794+
}
795+
774796
// No matching CLP, let's check the Pointer permissions
775797
// And handle those later
776798
let permissionField = ['get', 'find'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields';

0 commit comments

Comments
 (0)