Skip to content

Commit a5cb179

Browse files
committed
Improved fetch and delete PFObject validation logic.
1 parent 2882c8e commit a5cb179

File tree

6 files changed

+86
-63
lines changed

6 files changed

+86
-63
lines changed

Parse/Internal/Object/BatchController/PFObjectBatchController.m

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ - (BFTask *)deleteObjectsAsync:(NSArray *)objects withSessionToken:(NSString *)s
150150
- (PFRESTCommand *)_deleteCommandForObjects:(NSArray *)objects withSessionToken:(NSString *)sessionToken {
151151
NSMutableArray *commands = [NSMutableArray arrayWithCapacity:objects.count];
152152
for (PFObject *object in objects) {
153-
[object checkDeleteParams];
154153
PFRESTCommand *deleteCommand = [PFRESTObjectCommand deleteObjectCommandForObjectState:object._state
155154
withSessionToken:sessionToken];
156155
[commands addObject:deleteCommand];

Parse/Internal/Object/Controller/PFObjectController.m

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,11 @@ + (instancetype)controllerWithDataSource:(id<PFCommandRunnerProvider>)dataSource
5252

5353
- (BFTask *)fetchObjectAsync:(PFObject *)object withSessionToken:(NSString *)sessionToken {
5454
@weakify(self);
55-
return [[[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{
55+
return [[[[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{
56+
return [object _validateFetchAsync];
57+
}] continueWithSuccessBlock:^id(BFTask *task) {
5658
@strongify(self);
57-
PFObjectState *state = [object._state copy];
58-
if (!state.objectId) {
59-
NSError *error = [PFErrorUtilities errorWithCode:kPFErrorMissingObjectId
60-
message:@"Can't fetch an object that hasn't been saved to the server."];
61-
return [BFTask taskWithError:error];
62-
}
63-
PFRESTCommand *command = [PFRESTObjectCommand fetchObjectCommandForObjectState:state
59+
PFRESTCommand *command = [PFRESTObjectCommand fetchObjectCommandForObjectState:[object._state copy]
6460
withSessionToken:sessionToken];
6561
return [self _runFetchCommand:command forObject:object];
6662
}] continueWithSuccessBlock:^id(BFTask *task) {
@@ -89,7 +85,9 @@ - (BFTask *)processFetchResultAsync:(NSDictionary *)result forObject:(PFObject *
8985

9086
- (BFTask *)deleteObjectAsync:(PFObject *)object withSessionToken:(nullable NSString *)sessionToken {
9187
@weakify(self);
92-
return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{
88+
return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{
89+
return [object _validateDeleteAsync];
90+
}] continueWithSuccessBlock:^id(BFTask *task) {
9391
@strongify(self);
9492
PFObjectState *state = [object._state copy];
9593
if (!state.objectId) {

Parse/Internal/Object/PFObjectPrivate.h

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,6 @@
4646
objectId:(NSString *)objectId
4747
isComplete:(BOOL)complete;
4848

49-
///--------------------------------------
50-
/// @name Validation
51-
///--------------------------------------
52-
53-
/*!
54-
Validate the save eventually operation with the current state.
55-
The result of this task is ignored. The error/cancellation/exception will prevent `saveEventually`.
56-
57-
@returns Task that encapsulates the validtion.
58-
*/
59-
- (BFTask *)_validateSaveEventuallyAsync;
60-
6149
@end
6250

6351
///--------------------------------------
@@ -93,6 +81,21 @@
9381

9482
#endif
9583

84+
///--------------------------------------
85+
/// @name Validation
86+
///--------------------------------------
87+
88+
- (BFTask PF_GENERIC(PFVoid) *)_validateFetchAsync NS_REQUIRES_SUPER;
89+
- (BFTask PF_GENERIC(PFVoid) *)_validateDeleteAsync NS_REQUIRES_SUPER;
90+
91+
/*!
92+
Validate the save eventually operation with the current state.
93+
The result of this task is ignored. The error/cancellation/exception will prevent `saveEventually`.
94+
95+
@returns Task that encapsulates the validation.
96+
*/
97+
- (BFTask PF_GENERIC(PFVoid) *)_validateSaveEventuallyAsync NS_REQUIRES_SUPER;
98+
9699
///--------------------------------------
97100
/// @name Pin
98101
///--------------------------------------
@@ -163,7 +166,6 @@
163166
///--------------------------------------
164167
#pragma mark - Validations
165168
///--------------------------------------
166-
- (void)checkDeleteParams;
167169
- (void)_checkSaveParametersWithCurrentUser:(PFUser *)currentUser;
168170
/*!
169171
Checks if Parse class name could be used to initialize a given instance of PFObject or it's subclass.

Parse/PFInstallation.m

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#import "PFPushPrivate.h"
2828
#import "PFQueryPrivate.h"
2929
#import "Parse_Private.h"
30+
#import "PFErrorUtilities.h"
3031

3132
@implementation PFInstallation (Private)
3233

@@ -51,9 +52,12 @@ - (void)_clearDeviceToken {
5152
[super removeObjectForKey:PFInstallationKeyDeviceToken];
5253
}
5354

54-
// Check security on delete.
55-
- (void)checkDeleteParams {
56-
PFConsistencyAssert(NO, @"Installations cannot be deleted.");
55+
- (BFTask<PFVoid> *)_validateDeleteAsync {
56+
return [[super _validateDeleteAsync] continueWithSuccessBlock:^id(BFTask<PFVoid> *task) {
57+
NSError *error = [PFErrorUtilities errorWithCode:kPFErrorCommandUnavailable
58+
message:@"Installation cannot be deleted"];
59+
return [BFTask taskWithError:error];
60+
}];
5761
}
5862

5963
// Validates a class name. We override this to only allow the installation class name.

Parse/PFObject.m

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -797,11 +797,6 @@ - (BOOL)isDataAvailableForKey:(NSString *)key {
797797
#pragma mark - Validations
798798
///--------------------------------------
799799

800-
// Validations that are done on delete. For now, there is nothing.
801-
- (void)checkDeleteParams {
802-
return;
803-
}
804-
805800
// Validations that are done on save. For now, there is nothing.
806801
- (void)_checkSaveParametersWithCurrentUser:(PFUser *)currentUser {
807802
return;
@@ -1527,8 +1522,6 @@ - (BFTask *)fetchAsync:(BFTask *)toAwait {
15271522
}
15281523

15291524
- (BFTask *)deleteAsync:(BFTask *)toAwait {
1530-
[self checkDeleteParams];
1531-
15321525
PFCurrentUserController *controller = [[self class] currentUserController];
15331526
return [[controller getCurrentUserSessionTokenAsync] continueWithBlock:^id(BFTask *task) {
15341527
NSString *sessionToken = task.result;
@@ -1564,11 +1557,7 @@ - (PFRESTCommand *)_constructSaveCommandForChanges:(PFOperationSet *)changes
15641557
}
15651558

15661559
- (PFRESTCommand *)_currentDeleteCommandWithSessionToken:(NSString *)sessionToken {
1567-
@synchronized (lock) {
1568-
[self checkDeleteParams];
1569-
return [PFRESTObjectCommand deleteObjectCommandForObjectState:self._state
1570-
withSessionToken:sessionToken];
1571-
}
1560+
return [PFRESTObjectCommand deleteObjectCommandForObjectState:self._state withSessionToken:sessionToken];
15721561
}
15731562

15741563
///--------------------------------------
@@ -1793,9 +1782,24 @@ + (PFObjectState *)_newObjectStateWithParseClassName:(NSString *)className
17931782
return [PFObjectState stateWithParseClassName:className objectId:objectId isComplete:complete];
17941783
}
17951784

1796-
#pragma mark Validation
1785+
///--------------------------------------
1786+
#pragma mark - Validation
1787+
///--------------------------------------
17971788

1798-
- (BFTask *)_validateSaveEventuallyAsync {
1789+
- (BFTask PF_GENERIC(PFVoid) *)_validateFetchAsync {
1790+
if (!self._state.objectId) {
1791+
NSError *error = [PFErrorUtilities errorWithCode:kPFErrorMissingObjectId
1792+
message:@"Can't fetch an object that hasn't been saved to the server."];
1793+
return [BFTask taskWithError:error];
1794+
}
1795+
return [BFTask taskWithResult:nil];
1796+
}
1797+
1798+
- (BFTask PF_GENERIC(PFVoid) *)_validateDeleteAsync {
1799+
return [BFTask taskWithResult:nil];
1800+
}
1801+
1802+
- (BFTask PF_GENERIC(PFVoid) *)_validateSaveEventuallyAsync {
17991803
return [BFTask taskWithResult:nil];
18001804
}
18011805

@@ -2002,9 +2006,10 @@ - (void)saveEventually:(PFBooleanResultBlock)block {
20022006
- (BFTask *)deleteEventually {
20032007
return [[[_eventuallyTaskQueue enqueue:^BFTask *(BFTask *toAwait) {
20042008
NSString *sessionToken = [PFUser currentSessionToken];
2005-
return [toAwait continueAsyncWithBlock:^id(BFTask *task) {
2009+
return [[toAwait continueAsyncWithBlock:^id(BFTask *task) {
2010+
return [self _validateDeleteAsync];
2011+
}] continueWithSuccessBlock:^id(BFTask *task) {
20062012
@synchronized (lock) {
2007-
[self checkDeleteParams];
20082013
_deletingEventually += 1;
20092014

20102015
PFOfflineStore *store = [Parse _currentManager].offlineStore;
@@ -2462,12 +2467,18 @@ + (BOOL)deleteAll:(NSArray *)objects error:(NSError **)error {
24622467
NSArray *uniqueObjects = [PFObjectBatchController uniqueObjectsArrayFromArray:deleteObjects usingFilter:^BOOL(PFObject *object) {
24632468
return (object.objectId != nil);
24642469
}];
2465-
[uniqueObjects makeObjectsPerformSelector:@selector(checkDeleteParams)]; // TODO: (nlutsenko) Make it async?
2466-
return [self _enqueue:^BFTask *(BFTask *toAwait) {
2467-
return [toAwait continueAsyncWithBlock:^id(BFTask *task) {
2468-
return [[self objectBatchController] deleteObjectsAsync:uniqueObjects withSessionToken:sessionToken];
2469-
}];
2470-
} forObjects:uniqueObjects];
2470+
NSMutableArray PF_GENERIC(BFTask <PFVoid> *) *validationTasks = [NSMutableArray array];
2471+
for (PFObject *object in uniqueObjects) {
2472+
[validationTasks addObject:[object _validateDeleteAsync]];
2473+
}
2474+
return [[BFTask taskForCompletionOfAllTasks:validationTasks] continueWithSuccessBlock:^id(BFTask *task) {
2475+
return [self _enqueue:^BFTask *(BFTask *toAwait) {
2476+
return [toAwait continueAsyncWithBlock:^id(BFTask *task) {
2477+
return [[self objectBatchController] deleteObjectsAsync:uniqueObjects
2478+
withSessionToken:sessionToken];
2479+
}];
2480+
} forObjects:uniqueObjects];
2481+
}];
24712482
}] continueWithSuccessResult:@YES];
24722483
}
24732484

Parse/PFUser.m

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,32 @@ + (BFTask *)_getCurrentUserSessionTokenAsync {
9292
#pragma mark - PFObject
9393
///--------------------------------------
9494

95-
// Check security on delete
96-
- (void)checkDeleteParams {
97-
PFConsistencyAssert(self.isAuthenticated, @"User cannot be deleted unless they have been authenticated via logIn or signUp");
98-
[super checkDeleteParams];
95+
#pragma mark Validation
96+
97+
- (BFTask PF_GENERIC(PFVoid) *)_validateDeleteAsync {
98+
return [[super _validateDeleteAsync] continueWithSuccessBlock:^id(BFTask<PFVoid> *task) {
99+
if (!self.isAuthenticated) {
100+
NSError *error = [PFErrorUtilities errorWithCode:kPFErrorUserCannotBeAlteredWithoutSession
101+
message:@"User cannot be deleted unless they have been authenticated."];
102+
return [BFTask taskWithError:error];
103+
}
104+
return nil;
105+
}];
106+
}
107+
108+
- (BFTask PF_GENERIC(PFVoid) *)_validateSaveEventuallyAsync {
109+
return [[super _validateSaveEventuallyAsync] continueWithSuccessBlock:^id(BFTask<PFVoid> *task) {
110+
if ([self isDirtyForKey:PFUserPasswordRESTKey]) {
111+
NSError *error = [PFErrorUtilities errorWithCode:kPFErrorOperationForbidden
112+
message:@"Unable to saveEventually a PFUser with dirty password."];
113+
return [BFTask taskWithError:error];
114+
}
115+
return nil;
116+
}];
99117
}
100118

119+
#pragma mark Else
120+
101121
- (NSString *)displayClassName {
102122
if ([self isMemberOfClass:[PFUser class]]) {
103123
return @"PFUser";
@@ -1202,15 +1222,4 @@ + (PFObjectState *)_newObjectStateWithParseClassName:(NSString *)className
12021222
return [PFUserState stateWithParseClassName:className objectId:objectId isComplete:complete];
12031223
}
12041224

1205-
#pragma mark Validation
1206-
1207-
- (BFTask *)_validateSaveEventuallyAsync {
1208-
if ([self isDirtyForKey:PFUserPasswordRESTKey]) {
1209-
NSError *error = [PFErrorUtilities errorWithCode:kPFErrorOperationForbidden
1210-
message:@"Unable to saveEventually a PFUser with dirty password."];
1211-
return [BFTask taskWithError:error];
1212-
}
1213-
return [BFTask taskWithResult:nil];
1214-
}
1215-
12161225
@end

0 commit comments

Comments
 (0)