Increment counter for currentUser - objective-c

I would like to increment a counter for a given currentUser's key.
I'm using the incrementKey method, however, the following code is not working*:
[currentUser incrementKey:#"likes" byAmount:[NSNumber numberWithInt:1]];
[currentUser saveInBackground];
It doesn't raise an error, but it doesn't update the database either.

Supposing that current User is a PFUser instance, if it is not the current logged user [PFUser currentUser], probably you should have the right ACL on that user row.
Try to set the current ACL field for that user for read and write, eg:
// set user acl
PFACL* userACL = [PFACL ACLWithUser:currentUser];
[userACL setPublicReadAccess:YES];
[userACL setPublicWriteAccess:YES];
[currentUser setACL:userACL];
[currentUser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
// check if any save error occurs
}];
Honestly, i suggest to don't use the above approach for security reason, especially on the User table. Instead you could try to make a custom function Cloud code side, using the Parse.Cloud.useMasterKey() and so increment the user with something like
.
..
...
Parse.Cloud.useMasterKey();
...
// retrieve the user with passed user object id from client
var userId =request.params.myUserId;
var user = new Parse.User();
user.set("objectId", userId);
user.fetch().then(function(concreteUser){
concreteUser.increment("likes", 1);
concreteUser.save().then(function(incrementedUser){
response.success();
},function(saveError){
response.error(saveError);
});
},function(error){
response.error(error);
});
Hope it helps

Related

Authorization session always ask for user password in helper

I have an Objective-C application (https://github.com/NBICreator/NBICreator) with a privileged helper tool.
I have a few different privileged tasks the helper will need to perform during one build, and I want to have the user authenticate only once to perform those tasks.
The authorization works, but I can't seem to reuse the session in the helper. The user always have to authenticate for every step, even if I supply the exact same right to the Security Server and use the same AuthenticationRef.
I have read the docs and tested the pre-authentication methods in the main app first, and tried printing out (and retaining the auth session in the helper as well). But nothing I've tried have yet to work successfully.
I need som help figuring out why the Security Server feel the need to reauthenticate.
The code on GitHub in the Master Branch is current, and what I'm trying and testing by changing things back and forth. With that code, the user have to authenticate each time I call a helper function, even if I use the same authentication right.
This is what the right looks like in the authorization database:
class = rule;
created = "470329367.933364";
"default-prompt" = {
"" = "NBICreator is trying to start an Imagr workflow.";
};
identifier = "com.github.NBICreator";
modified = "470329367.933364";
requirement = "identifier \"com.github.NBICreator\" and anchor apple generic and certificate leaf[subject.CN] = \"Mac Developer: Erik Berglund (BXUF2UUW7E)\" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */";
rule = (
"authenticate-admin"
);
version = 0;
This is where I define the right: https://github.com/NBICreator/NBICreator/blob/master/NBICreator/Helper/NBCHelperAuthorization.m#L36-L43
NSStringFromSelector(#selector(authorizeWorkflowImagr:withReply:)) : #{
kCommandKeyAuthRightName : #"com.github.NBICreator.workflowImagr",
kCommandKeyAuthRightDefault : #kAuthorizationRuleAuthenticateAsAdmin,
kCommandKeyAuthRightDesc : NSLocalizedString(
#"NBICreator is trying to start an Imagr workflow.",
#"prompt shown when user is required to authorize to add a user"
)
},
And this is where I check if the user is authenticated:
https://github.com/NBICreator/NBICreator/blob/master/NBICreator/Helper/NBCHelperAuthorization.m#L222-L253
+ (NSError *)checkAuthorization:(NSData *)authData command:(SEL)command authRef:(AuthorizationRef)authRef {
#pragma unused(authData)
NSError * error;
OSStatus err = 0;
AuthorizationItem oneRight = { NULL, 0, NULL, 0 };
AuthorizationRights rights = { 1, &oneRight };
oneRight.name = [#"com.github.NBICreator.workflowImagr" UTF8String];
err = AuthorizationCopyRights(
authRef,
&rights,
NULL,
kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed,
NULL
);
if ( err != errAuthorizationSuccess ) {
NSString *message = CFBridgingRelease(SecCopyErrorMessageString(err, NULL));
error = [NSError errorWithDomain:[[NSProcessInfo processInfo] processName] code:err userInfo:#{ NSLocalizedDescriptionKey : message }];
}
return error;
}
As you can see there, I'm testing by setting a hardcoded right name to try and resue that right to the Security Server.
I'm stumped right now, and can't seem to find a way forward. Hoping someone here might know where to look.
I found the answer in how I set up the right in the rights database.
I was using the default code from the Apple example EvenBetterAuthorizationExample for creating and using the rights. But that only points to this documentation for the different rights to use.
None of those were helping me with authenticatin once and the having the helper be authenticated the next time I request authentication for the same right.
After some digging and looking into the actual rules in the authorization database I found a way to copy the rule that AuthenticateWithPrivileges uses, that works by authenticating for 5 minutes until requiring re-authentication.
So, I changed my code for creating my custom right from this:
NSStringFromSelector(#selector(addUsersToVolumeAtPath:userShortName:userPassword:authorization:withReply:)) : #{
kCommandKeyAuthRightName : NBCAuthorizationRightAddUsers,
kCommandKeyAuthRightDefault : #kAuthorizationRuleAuthenticateAsAdmin,
kCommandKeyAuthRightDesc : NSLocalizedString(
#"NBICreator is trying to add a user.",
#"prompt shown when user is required to authorize to add a user"
)
},
To this:
NSStringFromSelector(#selector(addUsersToVolumeAtPath:userShortName:userPassword:authorization:withReply:)) : #{
kCommandKeyAuthRightName : NBCAuthorizationRightAddUsers,
kCommandKeyAuthRightDefault : #{
#"class": #"user",
#"group": #"admin",
#"timeout": #(300),
#"version": #(1),
},
kCommandKeyAuthRightDesc : NSLocalizedString(
#"NBICreator is trying to add a user.",
#"prompt shown when user is required to authorize to add a user"
)
},
So, instead of using #kAuthorizationRuleAuthenticateAsAdmin as the RightDefault, i passed in this dict:
#{
#"class": #"user",
#"group": #"admin",
#"timeout": #(300),
#"version": #(1),
},
After that, my helper can request authorization from the user, and then I can reuse that session for the same right during 5 minutes without asking the user again.

Increment Likes in Objective-C / Parse Cloud Code

I am making a social app using Objective-C and Parse. I am trying to write cloud code to increment likes (which is part of the secured User class) upon a button click. This is what I have as of now:
Cloud Code
Parse.Cloud.define("incrementLikes", function(request, response) {
Parse.Cloud.useMasterKey();
var user = new Parse.Object("User");
user.userName = request.params.userName;
user.increment("profilePictureLikes");
});
profilePictureLikes is the name of the database row within the User class that is a number.
Objective-C
- (void)incrementLikes {
[PFCloud callFunctionInBackground:#"incrementLikes" withParameters:#{#"userName": self.user.username}];
}
self.user is the PFUser whose profile is being viewed.
This code generates the following error, and does not increment the likes:
[Error]: success/error was not called (Code: 141, Version: 1.6.2)
Well, the error is because you aren't calling the response handler in the cloud code but you have a number of other issues:
Not calling the response handler
Creating a new user instead of querying to find the existing user
Passing the user name instead of the user object id (see here)
Not saving the user after updating it

Parse.com procedure for validating password requirements in Parse.Cloud.beforeSave(Parse.User

I am trying to find the best procedure for adding password requirements with Parse.com. It appears the easiest way would be to use a cloud function to execute right before user data is saved. My only caveat is that I want to validate user passwords only when the password is different from what is stored in the db or if the user does not exist in the db.
Parse.Cloud.beforeSave(Parse.User, function(request, response) {
...
}
Couple of questions:
Does request.object.existed() work in beforeSave functions as it does with afterSave?
Can I access the user's password (using Parse.Cloud.useMasterKey()) via the request.object in the beforeSave functions? Or do I need to query the user's object inside the function.
In the log the input to the beforeSave function appears to have original and updated keys similar to the json below. However, I was not able to access the original and update json through the request.object. How would I access this data? It would be nice if the only check needed to be performed to verify whether a user's password as changed if a comparison between request.object.original.password !== request.object.updated.password
Sample cloudcode log output:
Input: {"original":{"email":"blah",
"firstname" : "blah",
"emailVerified":true,
"username":"blah",
"createdAt":"2014-04-28T23:05:47.452Z",
"updatedAt":"2014-0716T01:55:52.907Z",
"objectId":"blah",
"sessionToken":"blah"},
"update":{"firstname":"blah2"}}
Try something like this:
Parse.Cloud.beforeSave(Parse.User, function(request, response) {
if (request.object.isNew()) {
// new object..
response.success();
} else {
if (request.object.dirtyKeys().indexOf("password") > -1) {
// Attempted to change the password.
response.error('No.');
}
}
});

google account login for the first time displays error

I am trying to login the user with google account in my application. I am having a problem when the user is logged in for the first time with google account in my app it shows this error:
Argument 1 passed to Illuminate\Auth\Guard::login() must implement interface Illuminate\Auth\UserInterface, null given
In my controller I have this:
public function loginWithGoogle() {
// get data from input
$code = Input::get('code');
// get google service
$googleService = Artdarek\OAuth\Facade\OAuth::consumer("Google");
if (!empty($code)) {
// This was a callback request from google, get the token
$token = $googleService->requestAccessToken($code);
// Send a request with it
$result = json_decode($googleService->request('https://www.googleapis.com/oauth2/v1/userinfo'), true);
$user = User::whereEmail($result['email'])->first(['id']);
if (empty($user)) {
$data = new User;
$data->Username = $result['name'];
$data->email = $result['email'];
$data->google_id = $result['id'];
$data->first_name = $result['given_name'];
$data->last_name = $result['family_name'];
$data->save();
}
Auth::login($user);
return Redirect::to('/');
}
// if not ask for permission first
else {
// get googleService authorization
$url = $googleService->getAuthorizationUri();
// return to facebook login url
return Redirect::to((string) $url);
}
}
I know the problem is with Auth::login($user); as insert is performed at the same time with Auth::login($user); and it doesn't find data from database for the first time, but I don't know how to avoid this error and instead redirects to the main page even when the user is logged in for the first time. After this error the user is logged in, but how to avoid this?
Without knowing whether the rest of the code works, you definitely have a problem here:
if (empty($user)) {
$data = new User;
(...)
$data->save();
}
Auth::login($user);
When you're done creating your user, the $user variable is still empty. Your user is actually called $data. You should either rename the variable, or do the login with $data instead. Hopefully, that's enough to make the code work. :)

viaRemember not work - laravel

Auth :: attempt works perfect, but when you pass the second parameter "true" apparently does not care or does not recover with viaRemember
viaRemember fails to work, check this
controller User
`$`userdata = array(
'email' => trim(Input::get('username')),
'password' => trim(Input::get('password'))
);
if(Auth::attempt(`$`userdata, true)){
return Redirect::to('/dashboard');
}
view 'dashboard', always show 777
#if (Auth::viaRemember())
{{666}}
#else
{{777}}
#endif
I have hit the same obstacle, so looking into the code one can see that viaRemember is not meant to be used as a function to check if the user was logged into the system in one of all the ways a user can be logged in.
'viaRemember' is meant to check if a user was logged into the system specifically via the `viaRemember' cookie.
From what I gather, authentication of user is remembered in two ways:
a via remember cookie.
The cookie value is compared to the via remember field in the users table.
a session cookie.
The cookie value is used in the server to get the session from the
session store. On the session object from the store there is data attached. One of the
data items is the user id connected to the session. The first time
the session was created, the system attached the user id to the data
of the season.
In Illuminate\Auth\Guard class:
public function user()
{
if ($this->loggedOut) return;
// If we have already retrieved the user for the current request we can just
// return it back immediately. We do not want to pull the user data every
// request into the method because that would tremendously slow an app.
if ( ! is_null($this->user))
{
return $this->user;
}
$id = $this->session->get($this->getName());
// First we will try to load the user using the identifier in the session if
// one exists. Otherwise we will check for a "remember me" cookie in this
// request, and if one exists, attempt to retrieve the user using that.
$user = null;
if ( ! is_null($id))
{
$user = $this->provider->retrieveByID($id);
}
// If the user is null, but we decrypt a "recaller" cookie we can attempt to
// pull the user data on that cookie which serves as a remember cookie on
// the application. Once we have a user we can return it to the caller.
$recaller = $this->getRecaller();
if (is_null($user) && ! is_null($recaller))
{
$user = $this->getUserByRecaller($recaller);
}
return $this->user = $user;
}
The getUserByRecaller function is called only if the session cookie authentication did not work.
The viaRemember flag is only set in the getUserByRecaller function. The viaRemember method is only a simple getter method.
public function viaRemember()
{
return $this->viaRemember;
}
So in the end, we can use Auth::check() that does make all the checks including the viaRemember check. It calls the user() function in the Guard class.
It seems also the viaRemember is only an indicator. You need to do a type of Auth::check() the will get the process of authentication started and so the user() function will be called.
It seems that your project is on Laravel 4.0 but viaRemember() is added in Laravel 4.1! So that's expected.
in config\session.php file change the 'expire_on_close' = false to true and once you close restart your browser, it must be ok.