I am developing an android application using google endpoints and google app engine. My backend does not seem to actually be doing anything. It appears as if nothing is being saved to the datastore and therefore nothing can be retrieved from it.
Here are some of the Api methods I have written in endpoints that are not working:
private static String getUserId(User user) {
String userId = user.getUserId();
if (userId == null) {
AppEngineUser appEngineUser = new AppEngineUser(user);
ofy().save().entity(appEngineUser).now();
// Begin new session for not using session cache.
Objectify objectify = ofy().factory().begin();
AppEngineUser savedUser = objectify.load().key(appEngineUser.getKey()).now();
userId = savedUser.getUser().getUserId();
}
return userId;
}
#ApiMethod(name = "saveProfile", path = "profile", httpMethod = ApiMethod.HttpMethod.POST)
public Profile saveProfile(final User user, final ProfileForm profileForm) throws UnauthorizedException {
if(user == null) {
throw new UnauthorizedException("Authorization required.");
}
String firstName = profileForm.getFirstName();
String surname = profileForm.getLastName();
String userEmail = user.getEmail();
int year = profileForm.getYear();
int month = profileForm.getMonth();
int day = profileForm.getDay();
Profile profile = ofy().load().key(Key.create(Profile.class, getUserId(user))).now();
if (profile == null) {
// the user does not have a profile and is creating one for the first time
profile = new Profile(getUserId(user), firstName, surname, userEmail, year, month, day);
} else {
profile.update(firstName, surname, userEmail, year, month, day);
}
ofy().save().entity(profile).now();
return profile;
}
#ApiMethod(name = "getProfile", path = "profile", httpMethod = ApiMethod.HttpMethod.GET)
public Profile getProfile(User user) throws UnauthorizedException {
if (user == null) {
throw new UnauthorizedException("Authentication required.");
}
return ofy().load().key(Key.create(Profile.class, getUserId(user))).now();
}
}
The profile class has the #Entity annotation and is registered with objectify in a static block like so:
static {
factory().register(AppEngineUser.class);
factory().register(Profile.class);
}
The userId is generated by GAE through
com.google.appengine.api.users.User
and the userId property is a String with the #Index annotation.
I am also confused by the api explorer and how it is responding to these methods. Whenever I call the saveProfile api method, a profile object is returned with a userId of 0 and an email of "example#example.com" although I believe this is the default email when running on localhost.
I am also running api explorer over HTTP,Google says this "can cause problems." Is this the reason why nothing is working. I have had to load unsafe scripts just for me to use my api, but maybe it does not work as it is hosted over HTTP instead of HTTPS.
Is this entire problem of not being able to fully test my methods due to a fundamental flaw in my understanding of GAE or is due to me running on localhost. If it is the latter perhaps I should deploy to Appspot and things may run smoother.
If there is anything extra you need to help, please just ask.
Thank you!
Check your logs in the developers console. It records all API methods that you execute and will show if any have any errors.
Since you are getting example#example.com as the email of the User this leads me to believe the User is not being injected by GAE. This is probably because you are doing something wrong client side (e.g. in Android). Make sure your Android app correctly asks to log a user in with Google and pass those credentials to your builder object in Android.
If you are executing your API method via the api explorer, you need to be logged in as a google user first for that User object to be populated in your method (I think you already know that).
In short, check your logs and your client code.
Related
I am a newbie (6 months going or so) and creating an app on Android (Java) that utilizes FireBase Auth with Google Sign In. (with only a few days of NodeJS exposure now) In other words my end user signs in with the Google Account. That part (I think) works pretty well so far. I use the Firestore Database heavily for a lot of things in the app.
So now I've gotten to the point where I want to use (Callable) Cloud Functions with HTTP Triggers. (never having done any of this before) I'm trying to get a proof of concept working at this time. The actual function works and I can trigger it from my app.
It appears that I cannot figure out how to make the function "private" though; as in "adding proper Members" to the Cloud function who have the right to invoke the function.
I have tried a few different things by trial error, but first let me show what I have.
This is the Cloud Function and I'm passing in an arbitrary String as a test, works nicely: (as long as "allUsers" have the role/right to invoke the function; in other words when the function is public.
exports.createTest = functions.https.onCall((data, context) => {
const text = data.text;
const uid = context.auth.uid;
const name = context.auth.token.name || null;
const email = context.auth.token.email || null;
console.log('UID: ', uid);
console.log('Name: ', name);
console.log('Email: ', email);
console.log('Message: ', text);
});
The above function gets triggered in my Android/Java code like this: (I think this code came from Google Doc/Sample/Example
private FirebaseFunctions mFunctions;
...
private void testing() {
mFunctions = FirebaseFunctions.getInstance();
Log.e(LOG_TAG, "Testing executed!");
String testMessage = "Hello Hello Testing 123 Mic Check";
createTest(testMessage)
.addOnCompleteListener(new OnCompleteListener<String>() {
#Override
public void onComplete(#NonNull Task<String> task) {
if (!task.isSuccessful()) {
Exception e = task.getException();
if (e instanceof FirebaseFunctionsException) {
FirebaseFunctionsException ffe = (FirebaseFunctionsException) e;
FirebaseFunctionsException.Code code = ffe.getCode();
Object details = ffe.getDetails();
Log.e(LOG_TAG, "FFE: " + ffe.getMessage() );
Log.e(LOG_TAG, "Code: " + code);
Log.e(LOG_TAG, "Details:" + details);
}
// ...
}
// ...
}
});
}
private Task<String> createTest(String text) {
// Create the arguments to the callable function.
Map<String, Object> data = new HashMap<>();
data.put("text", text);
data.put("push", true);
return mFunctions
.getHttpsCallable("createTest") //this is the function name
.call(data)
.continueWith(new Continuation<HttpsCallableResult, String>() {
#Override
public String then(#NonNull Task<HttpsCallableResult> task) throws Exception {
// This continuation runs on either success or failure, but if the task
// has failed then getResult() will throw an Exception which will be
// propagated down.
String result = (String) task.getResult().getData();
if (result != null) {
Log.e(LOG_TAG, "Result: " + result);
}
return result;
}
});
}
Only when I have "allUsers" added with the role/right to "invoke Cloud Function" then I get this working. My understanding of HTTP Requests and such is pretty limited, which is not making things easier.
I tried using the "allAuthenticatedUsers" options, which I figured would do the trick, because I actually authenticate my Users in the app through Firebase/Google Sign In. This Cloud Function shall only be available to either a) authenticated users or b) users of a specific domain. (I have a domain, let's say #testorganization.com) Or if I can identify my particular app (api key?) then that would work, too.
The moment I add a member "allAuthenticatedUsers" with role to invoke the function (and remove "allUsers) nothing happens. I also tried adding the entire domain, but that wouldn't work. (duh) Also tried adding my service account (trial and error at this point) and didn't seem to work.
In my Node JS code I am actually receiving the UID of the authenticated user, so it appears that some kind of user authentication information is already being exchanged.
With that knowledge, I can (successfully tried this) get the UID and cross check that against my database and verify a user that way, but seems unnecessary and I should be able to make the permissions work. (lock the function down entirely) Plus this took a really long time just finish this cross check. Or is this pretty standard procedure to do?
Like this-->
const usersRef = admin.firestore().collection('users').doc(uid)
usersRef.get()
.then((docSnapshot) => {
if (docSnapshot.exists) {
usersRef.onSnapshot((doc) => {
console.log('User Type logged in: ', doc.data().userCategory)
console.log('User Title: ', doc.data().userTitle)
});
} else {
console.log('User does not exist')
}
});
Edit:
So while not having figured out how to shut down the function entirely, I did discover that instead of cross checking my users, I can simple check for auth like this:
if (context.auth){
//user is auth'd
} else {
//no auth
}
That's a little bit better, I guess. (but still doesn't technically prevent access to the function?!)
Thank you so much for any help. Much appreciated.
Edit2:
Here is a screensshot of the area in the cloud console (for cloud function roles/privileges) that I am referring to:
https://imgur.com/cBsjaaL
With a Callable Cloud Function, if you want to ensure that only authenticated users can trigger it's business logic, you actually don't need to configure any extra "cloud function roles/privileges" as shown at the bottom of your question.
By default, with a Callable Cloud Function you will get, when available, "Firebase Authentication and FCM tokens automatically included in requests" and it will "automatically deserializes the request body and validates auth tokens", as explained in the doc.
So you just have to follow the doc and use the context parameter. As you have mentioned in your question, you can check the user is authenticated by doing:
if (context.auth) {
//...
}
If you want to verify the user email, you would do:
exports.addMessage = functions.https.onCall((data, context) => {
const uid = context.auth.uid;
return admin.auth().getUser(uid)
.then(userRecord => {
const userEmail = userRecord.email;
//....
})
.catch(function(error) {
console.log('Error fetching user data:', error);
// Send back an error to the front end
// See https://firebase.google.com/docs/functions/callable#handle_errors
});
});
You will find more examples on how to "work with" users with the Admin SDK here in the doc.
I develop a java application with spring. By the help of google analytics i want to know how to display user log information like how many users are accessing that application.For that i have a code to display all google analytics information as below.
final String requestURI = request.getRequestURI();
Long userId = null;
if ( zerpSession != null ) {
userId = zerpSession.getUserId();
}
GoogleAnalytics ga = new GoogleAnalytics("UA-5555555555-1");
DefaultRequest req = new DefaultRequest() ;
if (userId != null) {
req.userId(userId.toString());
}
ga.setDefaultRequest(req);
ga.postAsync(new RequestProvider() {
public GoogleAnalyticsRequest getRequest() {
return new PageViewHit(requestURI, "URL Request");
}
});
Here i am not able to to understand that from where to get user Info.
I fetch that user id from the session. I am trying to check in google analytics it is showing Zero users are accessing your application. Please help me for that.
I would like to list available IP VM's in the new Azure portal using Java SDK.
Couple of years back in the good old classic portal, I had followed the usual management certificate procedure to access vm's,create vm's and work with Azure Endpoints.
Fast fwd now I see that they have used a new portal and new mechanisms to interact with Java SDK. I read somewhere in the above link that with the old way with certificates, I can manage only the class portal resources.
I'm trying to code a simple program which authenticates and lists the vm's of the new portal as a start. Seems like they have complicated it a lot.
I followed the below link to "Create service principal with password"
https://azure.microsoft.com/en-us/documentation/articles/resource-group-authenticate-service-principal/
Then I went to this link
https://azure.microsoft.com/en-us/documentation/samples/resources-java-manage-resource-group/
which asked me go the "See how to create an Auth file" link in above page
(mine is not a webapp and when I try to create the AD as a native client application, it is not allowing me to save keys in configure tab, so I had to create a web app)
After doing all this, I got stuck with this below error
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
'authority' Uri should have at least one segment in the path (i.e.https://<host>/<path>/...)
java.lang.IllegalArgumentException: 'authority' Uri should have at least one segment in the path (i.e. https://<host>/<path>/...)
at com.microsoft.aad.adal4j.AuthenticationAuthority.detectAuthorityType(AuthenticationAuthority.java:190)
at com.microsoft.aad.adal4j.AuthenticationAuthority.<init>(AuthenticationAuthority.java:73)
When I checked it says that the error is because I don't have a valid client application id in your Azure Active Directory.
Is there any simple way to authenticate and start using the API's?
#Vikram, I suggest that you can try to refer to the article to create an application on AAD.
Then you can follow the code below to get the access token for authentication.
// The parameters include clientId, clientSecret, tenantId, subscriptionId and resourceGroupName.
private static final String clientId = "<client-id>";
private static final String clientSecret = "<key>";
private static final String tenantId = "<tenant-id>";
private static final String subscriptionId = "<subscription-id>";
// The function for getting the access token via Class AuthenticationResult
private static AuthenticationResult getAccessTokenFromServicePrincipalCredentials()
throws ServiceUnavailableException, MalformedURLException, ExecutionException, InterruptedException {
AuthenticationContext context;
AuthenticationResult result = null;
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(1);
// TODO: add your tenant id
context = new AuthenticationContext("https://login.microsoftonline.com/" + tenantId, false, service);
// TODO: add your client id and client secret
ClientCredential cred = new ClientCredential(clientId, clientSecret);
Future<AuthenticationResult> future = context.acquireToken("https://management.azure.com/", cred, null);
result = future.get();
} finally {
service.shutdown();
}
if (result == null) {
throw new ServiceUnavailableException("authentication result was null");
}
return result;
}
String accessToken = getAccessTokenFromServicePrincipalCredentials().getAccessToken();
If you want to list the VMs on new portal, you can try to use the REST API List the resources in a subscription to get all resources and filter the VMs via the resource type Microsoft.Compute/virtualMachines.
Hope it helps.
Do you guys use the Google Cloud Platform, specifically the APP ENGINE ENDPOINTs for professional/production purposes? Asking because I am not comfortable with response time. It´s taking too long to perform simple http POST requests like registering an users with only e-mail and password (saving in a DATASTORE). In this URL you will find the register form, let me know what you guys thing about the response time, is it acceptable?
https://filiperebollo1986.appspot.com/register.html
My Java code is quite simple. Only instantiate an user object with email and password which does´t have any special encrypting algorithm. Naturally for the registering purpose I need to check if the user already exists and after that save the object in the DATASTORE. I can ensure that the problem is not in my front-end apps because takes too long in the iOS e Android apps too.
POST:
https://filiperebollo1986.appspot.com/_ah/api/igardenendpoints/v12/saveProfile.
{
"userEmail": "response_time",
"password": "123"
}
Response:
200
Show headers
{
"messageText": "User successfully created!",
"messageNumer": 0,
"kind": "igardenendpoints#resourcesItem",
"etag": "\"9e-bXvBAIkMzDxg9PvVcUBMIXB0/_RzWmXdehTtOTMg1Y7MhIvXy5-k\""
}
public Message saveProfile(ProfileForm profileForm) {
Message message;
String userEmail = profileForm.getUserEmail();
String password = profileForm.getPassword();
String displayName = profileForm.getDisplayName();
Profile profile = ofy().load().key(Key.create(Profile.class, profileForm.getUserEmail())).now();
if (profile == null) {
if (displayName == "") {
displayName = extractDefaultDisplayNameFromEmail(profileForm.getUserEmail());
}
profile = new Profile(userEmail, password, displayName);
message = new Message("User successfully created!", 0);
} else {
message = new Message("User already exists!", 1);
}
ofy().save().entity(profile).now();
return message;
}
I have the following Google Cloud Endpoints API, and I want it accesible only for authenticated users with certain role.
Few lines of code says much more than a thousand words:
import com.google.appengine.api.users.User;
#Api(
name = "heroesApi",
version = "v1",
clientIds = {...},
scopes = {...},
audiences = {...},
namespace = #ApiNamespace(
ownerDomain = "my.domain.com",
ownerName = "my.domain.com",
packagePath=""
)
)
public class HeroesApi {
// THIS IS THE FUNCTION THAT I DON´T KNOW WHERE TO PUT
/**
* Validates that the user is logged in with a specific role
* #param user
* #throws UnauthorizedException
* #throws ForbiddenException
*/
private void validateUser(User user) throws UnauthorizedException, ForbiddenException {
IUserController userController = ControllerFactory.get.userController();
if (user == null) {
throw new ForbiddenException("You must be logged in, my friend.");
} else if (!userController.isLoggedAsSuperHero(user)) {
throw new UnauthorizedException("You must be logged in as a Superhero.");
}
}
// API METHODS
#ApiMethod(path = "something", httpMethod = ApiMethod.HttpMethod.PUT)
public DoneTask doSomething(Some thing, User superhero) throws UnauthorizedException, ForbiddenException {
// this is what I want to avoid on each function
this.validateUser(superhero);
// Now I'll do something special
IUserController userController = ControllerFactory.get.userController();
return userController.doSome(thing);
}
// MORE GORGEOUS METHODS JUST FOR SUPERHEROES, SO I HAVE TO PERFORM THE VALIDATION...
}
I have though about adding a filter through the web.xml file for all request to /api/heroesApi/*, but the problem there is how to catch the Google Appengine Api User.
Isn´t there something provided by Google to do this?
Any suggestion to avoid the repeated call to validateUser(User) is welcome
Google Cloud Endpoints doesn't supper roles by default , so your application have to build role concept, and you have to write filter and get current user and check against the roles maintained in your end
To get current user in the filter, try this
https://cloud.google.com/appengine/docs/java/oauth/
I haven't tested this , but am sure you can get the current user using the UserService Api in appengine as long as your system uses google account as authentication.