azure java sdk authentication - java

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.

Related

How do you create ProjectApiRoot as Customer in Commercetools

I can create a ProjectApiRoot using the Java SDK and perform requests with that using the following code:
private static ProjectApiRoot createProjectClient() {
ProjectApiRoot apiRoot = ApiRootBuilder.of()
.defaultClient(ClientCredentials.of()
.withClientId(System.getenv("CTP_CLIENT_ID"))
.withClientSecret(System.getenv("CTP_CLIENT_SECRET"))
.build(),
ServiceRegion.GCP_EUROPE_WEST1)
.build(System.getenv("CTP_PROJECT_KEY"))
return apiRoot
}
However, I would like to authorize as a specific customer (email and password) and interact with the Commercetools API using the customer. The following code throws an error:
private static ProjectApiRoot createCustomerClient() {
def tokenUri = "https://auth.europe-west1.gcp.commercetools.com/oauth/*CTP_PROJECT_KEY*/customers/token"
def projectKey = System.getenv("CTP_PROJECT_KEY")
def scopes = System.getenv("CTP_SCOPES")
def credentials = ClientCredentials.of()
.withClientId("*email*")
.withClientSecret("*password*")
.withScopes(scopes)
.build()
def apiRootBuilder = ApiRootBuilder.of()
.withApiBaseUrl("https://api.europe-west1.gcp.commercetools.com")
.withClientCredentialsFlow(credentials, tokenUri)
return apiRootBuilder.build(projectKey)
}
Error:
io.vrap.rmf.base.client.oauth2.AuthException: detailMessage: Unauthorized
"message" : "Please provide valid client credentials using HTTP Basic Authentication.",
By using the withGlobalCustomerPasswordFlow instead of the withClientCredentialsFlow which authenticates the customer prior to doing the request.
But I would advise to do this only in a context where the customer is logging in everytime. Using it in any other context e.g. remembered log in of needs a more sophisticated approach as you need to store the bearer token and refresh token and can't easily use the middleware approach for authenticating the customer but instead do it not as part of an auth flow middleware.
Please see also https://github.com/commercetools/commercetools-sdk-java-v2/tree/main/commercetools/commercetools-sdk-java-api/src/integrationTest/java/commercetools/me

Java Design pattern: Avoid Duplication of code for Azure Rest api call

I have written a small java program which will make a rest call and it works fine as expected. I would. Now, I have to write a similar program for other API calls. How do, I structure the code so that, I can avoid duplicating the code!!
System.out.println( "Usage Details!" );
AzureProfile profile = new AzureProfile(AzureEnvironment.AZURE);
TokenCredential credential = new DefaultAzureCredentialBuilder()
.authorityHost(profile.getEnvironment()
.getActiveDirectoryEndpoint())
.build();
ConsumptionManager consumptionManager = ConsumptionManager.authenticate(credential, profile);
// the below detail changes for different api's
PagedIterable<UsageDetail> usageDetailList = consumptionManager.usageDetails()
.list("url",<argument1>,<argument2>,null,null,Metrictype.USAGE,Context.NONE);
int count=1;
for(UsageDetail usageDetail : usageDetailList){
LegacyUsageDetail legacyUsageDetail = (LegacyUsageDetail)usageDetail.innerModel();
try{
//if(legacyUsageDetail.date().toString().equals("2021-09-22T00:00Z") &&
legacyUsageDetail.resourceGroup().startsWith("F2BDEVC-ms")){
if(count==1){
System.out.println("subscriptionName : " +
legacyUsageDetail.subscriptionName());
}}
}catch(Exception e){}
}
You can use Azure Java SDK to call Azure rest API. The Azure SDK for Java libraries build on top of the underlying Azure REST API, allowing you to use those APIs through familiar Java paradigms.
To use Azure Java SDK to call Azure rest API first you need to create a service principal (SP). Then install the SDK as show below -
<!-- https://mvnrepository.com/artifact/com.microsoft.azure/azure -->
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure</artifactId>
<version>1.33.0</version>
</dependency>
Then write the code similar to the one show below.
private static String tenantId=""; // sp tenant
private static String clientId = ""; // sp appid
private static String clientKey = "";// sp password
private static String subscriptionId=""; //sp subscription id
ApplicationTokenCredentials creds = new ApplicationTokenCredentials(clientId,tenantId,clientKey, AzureEnvironment.AZURE);
RestClient restClient =new RestClient.Builder()
.withBaseUrl(AzureEnvironment.AZURE, AzureEnvironment.Endpoint.RESOURCE_MANAGER)
.withSerializerAdapter(new AzureJacksonAdapter())
.withReadTimeout(150, TimeUnit.SECONDS)
.withLogLevel(LogLevel.BODY)
.withResponseBuilderFactory(new AzureResponseBuilder.Factory())
.withCredentials(creds)
.build();
OkHttpClient httpClient = restClient.httpClient().newBuilder().build();
String url = "https://management.azure.com/subscriptions/"+subscriptionId+"/providers/Microsoft.Migrate/projects?api-version=2018-02-02";
Request request = new Request.Builder()
.url(url)
.method("get",null)
.build();
Response response1 = httpClient.newCall(request).execute();
if(response1.isSuccessful()){
System.out.println(response1.headers().toString());
}
For more information on design guidelines check this Java Azure SDK Design Guidelines.

Google app engine entities are not being created

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.

How do I access a Google Spreadsheet from App Engine using a Service Account?

I have an application on Google App Engine (hosted on appspot.com) which is going to be used by a few users to grab some stuff off the internet, save it in the Datastore and then write it to a Google Spreadsheet. I'm looking to use a Service Account to access the spreadsheet but I run into a 400 OK { "error" : "invalid_grant" } problem when I try to do anything, regardless of whether I try to create a file using the Drive API or access an existing file using Spreadsheets API.
I've turned on both Drive API and Drive SDK in the APIs Console and generated a P12 key. This is my method for building a GoogleCredential:
private GoogleCredential getGoogleCredential() throws IOException, GeneralSecurityException {
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(TRANSPORT)
.setJsonFactory(FACTORY)
.setServiceAccountId("1511XXX90247.apps.googleusercontent.com")
.setServiceAccountScopes(scopes)
.setServiceAccountPrivateKeyFromP12File(
new File("05a605b0fXXXd2a6be864be15d81a2bd629d3bd6-privatekey.p12"))
.setServiceAccountUser("XXX#gmail.com") // My personal e-mail address which I used to create the project
.build();
return credential;
}
The ServiceAccountID comes from the APIs Console. I've tried using 1511XXX90247#developer.gserviceaccount.com as my ServiceAccountUser as well as myappname#appspot.gserviceaccount.com. The scopes I'm using are as follows:
https://spreadsheets.google.com/feeds
https://docs.google.com/feeds
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.file
Here is the code that fails when I try to use Spreadsheets API:
SpreadsheetService service = new SpreadsheetService("name of my app");
service.setOAuth2Credentials(getGoogleCredential());
FeedURLFactory factory = FeedURLFactory.getDefault();
SpreadsheetQuery query = new SpreadsheetQuery(factory.getSpreadsheetsFeedUrl());
query.setTitleQuery("test");
SpreadsheetFeed feed = service.query(query, SpreadsheetFeed.class);
The code fails when the service tries execute the query.
Here's the Drive code I tried that also fails:
Drive drive = new Drive.Builder(TRANSPORT, FACTORY,
getGoogleCredential()).build();
com.google.api.services.drive.model.File file = new com.google.api.services.drive.model.File();
file.setTitle("test");
file.setMimeType("application/vnd.google-apps.spreadsheet");
drive.files().insert(file).execute(); // This is where the code fails
No matter what I try, I always seem to end up with the same ìnvalid grant` error. Here's a partial stacktrace:
com.google.gdata.util.AuthenticationException: Failed to refresh access token: 400 OK { "error" : "invalid_grant" }
at com.google.gdata.client.GoogleAuthTokenFactory$OAuth2Token.refreshToken(GoogleAuthTokenFactory.java:260)
at com.google.gdata.client.GoogleAuthTokenFactory.handleSessionExpiredException(GoogleAuthTokenFactory.java:702)
at com.google.gdata.client.GoogleService.handleSessionExpiredException(GoogleService.java:738) at com.google.gdata.client.GoogleService.getFeed(GoogleService.java:680)
at com.google.gdata.client.Service.query(Service.java:1237)
at com.google.gdata.client.Service.query(Service.java:1178)
at spam.gwt.scraper.server.spreadsheets.APIConnector.doGet(APIConnector.java:66)
I've tried to look for answers everywhere, including but not limited to the Google Developers Spreadsheets Guide, this Service Account-related Stack Overflow question and this Google App Engine-specific Stack Overflow question. What with the developments (eg. Docs -> Drive) it's hard to find up-to-date info, never mind info that pertains specifically to GAE.
I use the builtin Service Account of the App Engine application rather than creating my own; this way the private key is save (not stored locally, only on app engine).
For Drive:
protected Drive createDriveService() throws BookingSheetException {
HttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
GoogleClientRequestInitializer keyInitializer =
new CommonGoogleClientRequestInitializer(API_KEY);
AppIdentityCredential credential =
new AppIdentityCredential.Builder(Arrays.asList(DriveScopes.DRIVE)).build();
Drive service = new Drive.Builder(httpTransport, jsonFactory, null)
.setHttpRequestInitializer(credential)
.setGoogleClientRequestInitializer(keyInitializer)
.setApplicationName(appname).build();
return service;
}
For Spreadsheet:
private Credential creds;
/**
* Keep the expiration time of the access token to renew it before expiry
*/
private Calendar expirationTime = Calendar.getInstance();
protected void initCredentials() {
List<String> scopes = Arrays.asList("https://spreadsheets.google.com/feeds");
AppIdentityService appIdentity = AppIdentityServiceFactory.getAppIdentityService();
AppIdentityService.GetAccessTokenResult accessToken = appIdentity.getAccessToken(scopes);
expirationTime.setTime(accessToken.getExpirationTime());
expirationTime.add(Calendar.MINUTE,-REFRESH_MINUTES); //refresh some time before expiry
creds = new Credential(BearerToken.authorizationHeaderAccessMethod());
creds.setAccessToken(accessToken.getAccessToken());
}
public SpreadsheetUtil(String appname) {
myService = new SpreadsheetService(appname);
myService.setOAuth2Credentials(cred);
}
Make sure to creds.refreshToken() or reinitialize when they expire.

Error 403 "Access not configured" writing to bucket using JSON API

I am trying to write to Cloud Storage with the REST API using this code:
public static void insertData() {
try {
StorageObject st = new StorageObject();
//create the media object
Media m = new Media();
String content = "hi! this is a test";
m.setData(Base64.encodeBase64String(content.getBytes()));
m.setContentType("text/html");
st.setMedia(m);
//this gets me the credential, works for other APIs but not cloud storage
Storage storage = RequestBuilder.buildStorage();
//Create the insert and execute
Insert insert = storage.objects().insert("mybucket", st);
insert.execute();
} catch (IOException e) {
log.severe(e.getMessage());
}
}
This is my ACL entry as per the REST API:
"kind": "storage#bucketAccessControls",
"items": [
{
"kind": "storage#bucketAccessControl",
"id": "gammeprediction/allUsers",
"selfLink": "https://www.googleapis.com/storage/v1beta1/b/gammeprediction/acl/allUsers",
"bucket": "mybucket",
"entity": "allUsers",
"role": "OWNER"
}]
This is how I get the credential:
private static Credential authorize() {
GoogleCredential credential = null;
//load properties
Properties appProperties = new Properties();
appProperties.load(RequestBuilder.class
.getResourceAsStream("/app.properties"));
// creates an authorization with the key and service account given
InputStream is = RequestBuilder.class.getResourceAsStream("/"
+ appProperties.getProperty("app.keyFileName"));
PrivateKey pk;
try {
pk = PrivateKeys.loadFromKeyStore(KeyStore.getInstance("PKCS12"),
is, "notasecret", "privatekey", "notasecret");
credential = new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(
appProperties
.getProperty("app.serviceAccount"))
.setServiceAccountPrivateKey(pk)
.setServiceAccountScopes(PredictionScopes.PREDICTION,
DriveScopes.DRIVE, StorageScopes.DEVSTORAGE_FULL_CONTROL).build();
return credential;
}
The permissions on the bucket are OWNER for allUsers, but I still get a 403 Forbidden "Access not configured" error. What could possibly be wrong?
Once the JSON API is generally available, this logic will work.
However, at the moment, the JSON API is in Limited Preview. Since an unknown user is not considered to be a member of the limited preview, completely anonymous queries via the REST API are currently not possible. Instead, you must provide at a bare minimum a whitelisted API key when you connect. If you provide no further identity information, you'll be treated as an anonymous user. Or, going further, you can use OAuth2 credentials instead to be treated as a registered user. For more, see: https://developers.google.com/storage/docs/json_api/
Is that a GWT RequestBuilder? I'm not entirely familiar with its use, unfortunately. If it helps, here's an example of setting up a connection with an API key using the Google API Java Client: https://code.google.com/p/google-api-java-client/wiki/OAuth2#Unauthenticated_access
Also, it looks like your call to setData() is passing a non-base64'd string, which will fail.

Categories