my scenario is that two users can find each other through a MatchingService (hosted business logic) and then create a match/bond as a new entry in a new table. I want to make sure that these entries can only be created by the MatchingService and that only the matched users can see/find that specific entry. The business logic also has to be able to remove and update the match entry, but not one of the users directly. They always have to call through special service methods to do that.
DataPermission.FIND.grantForUser(first_user.getUserId(), match);
DataPermission.FIND.grantForUser(second_user.getUserId(), match);
But I cant’t even create the match in the server code so that part is never executed.
How can I achieve this? I have read quite some other posts here but no one has solved my problem.
You’re almost there… What’s missing is the permissions for either NotAuthenticatedUser or AuthenticatedUser. It is the combination of ServerCodeUser and one of the above that will give the ultimate permission to the table.
The reason I mentioned either NotAuthenticatedUser or AuthenticatedUser is you should choose whether business logic will be authenticating itself (logging in) as some special “admin” user, in that case the AuthenticatedUser should be granted access or will make calls without logging in (the NotAuthenticatedUser approach).
Since the roles for all other API keys are rejected access, it will ensure that only business logic can work with the table.
The user (with the roles: AuthenicatedUser, AndroidUser, FacebookUser and I guess SocialUser) accesses a service method getCreateMatch(id_of_the_other_one) in MathchingService. There I use something like the following to create the match:
Match match = new Match();
match.someField = "my value";
Backendless.Persistence.of(Match.class).save(match);
DataPermission.FIND.grantForUser(match.initiator.getUserId(), match);
DataPermission.FIND.grantForUser(match.receiver.getUserId(), match);
The client app invokes the Service with that method:
Sorry, I am confused now. I thought you said you wanted to give access to the table exclusively the business logic (server-code)? Now you say “The user (with the roles: AuthenicatedUser, AndroidUser, FacebookUser and I guess SocialUser)”, that’s not a server-code user, is it? Of course, there are no permissions for that user - look at the permissions for the listed role - they are denied access for the table.
Well, the user invokes the remote service method in the hosted service. I have a server-side class like this:
@Asset("*")
public class MatchingService implements IBackendlessService { ... }
with the getCreateMatch(…) method in it. There (in the server code) the match is created, saved and the permissions are set. At least in theory that should happen.
I looked into your app. Adding objects to the Match table is not a problem, it works - as you can see I created an object in there from my code. I suspect the problem is elsewhere. Does your MatchingService try creating an object in the Users table perhaps? I see you’re passing properties of the initiator and receiver into the service, which led to me believe the service would be creating Users before the Match is saved.
No I do not think so. I reduced my code to the following, without solving/changing the issue:
public Match getCreateMatch(String receiver_id) {
String initiator_id = InvocationContext.getUserId();
BackendlessUser initiator = Backendless.UserService.findById(initiator_id);
Match match = new Match();
// for this test i do not set the initiator or receiver fields
match.confirmed = false;
Backendless.Persistence.of(Match.class).save(match);
// this is never reached, instead a error is returned to the client:
// BackendlessFault{ code: '1011', message: 'User has no permission to create entity' }
throw new IllegalStateException("custom error");
// more code
}
The reason my original example worked is because I created a test user object which I used to login in the server-code and perform lookups of initiator and receiver.