Is there any way to set the Metadata Description?
https://developer.android.com/reference/com/google/android/gms/drive/Metadata.html#getDescription()
If so, what is the length limit?
I can't see anything in the api: https://developer.android.com/reference/com/google/android/gms/drive/MetadataChangeSet.Builder.html
Unfortunately not at the moment, AFAIK. What I do right now is initializing both GDAA and RESTful API (see the 'trash solution' SO 22295903) like this:
private GoogleApiClient _gac;
private com.google.api.services.drive.Drive _svc;
public GoogleApiClient init(String email){
_gac = new GoogleApiClient.Builder(UT.ACTX).addApi(com.google.android.gms.drive.Drive.API)
.addScope(com.google.android.gms.drive.Drive.SCOPE_FILE).setAccountName(email).build();
com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential crd =
GoogleAccountCredential.usingOAuth2(UT.ACTX,
Arrays.asList(com.google.api.services.drive.DriveScopes.DRIVE_FILE));
crd.setSelectedAccountName(email);
_svc = new com.google.api.services.drive.Drive.Builder(
AndroidHttp.newCompatibleTransport(), new GsonFactory(), crd).build();
return this;
}
You get the description from DGAA (GoogleApiClient _gac above), but update/write it to RESTFul like this (off UI thread):
public void oldDescUpW(String titl, String mime, String desc) {
try {
final FileList gLst = _svc.files().list()
.setQ("title = '"+titl+".jpg' and mimeType = '"+mime+"' and trashed = false")
.setFields("items(id)").execute();
if (gLst.getItems().size() == 1) {
final String sId = gLst.getItems().get(0).getId();
com.google.api.services.drive.model.File body =
new com.google.api.services.drive.model.File();
body.setDescription(desc);
_svc.files().patch(sId, body).execute();
}
} catch (Exception e) {}
}
It is also possible to use 'resource ID' from GDAA to address the file in RESTful, but it is not always immediately available (if the file is created in GDAA). See SO 22874657
DISCLAIMER:
It is a HACK and should not stay alive past GDAA delivery of an alternative.
Related
About 6 months ago I set up a web application in the google developers console so that employees of our internal web site could initiate emails which would read a template doc in a google account, merge some fields and then download a pdf version of it to email out.
Now we have to move those template docs to a different google managed domain/user account so I've made copies of the documents in the new account and updated our references with the new doc ids.
In addition, the email I had when I originally created this application in the google dev console is going away as of the first of the year. So I also have to recreate the app under a new account.
I've done that and matched all the settings of the original app. However, when I try to access a document I get the error Google.Apis.Auth.OAuth2.Responses.TokenResponseException: 'Error:"unauthorized_client", Description:"Unauthorized", Uri:""'
I had followed this page in setting up the original user authentication. I know there was a ton of trial and error before I actually got it working and I must be forgetting something. I'm wondering if it's tied to needing to reauthenticate the new app. Although I'm specifying the new clientid and clientsecret from the new app, I don't get the popup asking me to give permission to the app. I would expect with the new credential info that it would open that window asking me to give permission. Here's that file for reference. Any ideas?
public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
{
protected override FlowMetadata FlowData => new AppFlowMetadata();
}
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = AwsSecrets.GoogleCreds.ClientId,
ClientSecret = AwsSecrets.GoogleCreds.ClientSecret
},
Scopes = new[] {DriveService.Scope.Drive},
DataStore = new FileDataStore("Drive.Api.Auth.Store")
});
public override IAuthorizationCodeFlow Flow => flow;
public override string GetUserId(Controller controller)
{
return "userid";
}
}
public class GoogleController : TECWareControllerBase
{
private readonly IGoogleCredentialService _gservice;
public GoogleController(IGoogleCredentialService gservice)
{
_gservice = gservice;
}
public async Task<ActionResult> IndexAsync(CancellationToken cancellationToken)
{
var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).AuthorizeAsync(cancellationToken);
if (result.Credential != null)
{
_gservice.SaveRefreshToken(result.Credential.Token.RefreshToken);
return View();
}
return new RedirectResult(result.RedirectUri);
}
}
I finally found a way to get this working.
First off in this method
private static readonly IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = AwsSecrets.GoogleCreds.ClientId,
ClientSecret = AwsSecrets.GoogleCreds.ClientSecret
},
Scopes = new[] {DriveService.Scope.Drive},
DataStore = new FileDataStore("Drive.Api.Auth.Store")
});
I had to change the FileDataStore("Drive.Api.Auth.Store") key to something else like FileDataStore("GoogleAuth")
That forced the authentication to fire up.
Unfortunately, google then complained about an invalid redirect uri. The following code returned a redirect uri of http://localhost:11224/AuthCallback/IndexAsync which didn't even exist in my web application's Authorized redirect uris. It should have been http://localhost:11224/MVC/AuthCallback/IndexAsync. So in the url result's redirect url I changed it to what it should have been which allowed me to complete the authorization. Now I can access the documents in the authenticated account.
var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).AuthorizeAsync(cancellationToken);
I have an app running on Windows Phone 8.1 which calls a URL via InAppBrowser plugin. This URL is supposed to ask for the user certificate stored on a virtual smartcard on the phone.
When I call the URL via Internet Explorer, I am asked for my PIN to unlock the virtual smartcard but in the InAppBrowser, this doesn't work. No PIN prompt, nothing.
Iterating through the Certificates yielded from
IReadOnlyList<Certificate> certStores = await CertificateStores.FindAllAsync();
I can see the certificate at app runtime but InAppBrowser doesn't seem to query for them. Do I have to copy its reference to another certificate store or is InAppBrowser not capable of establishing SSL with user certificates ?
The issue is with the webview component, x-ms-webview to be more precisely. InAppBrowser plugin uses this component internally.
Found a workaround mentioned here, it kinda sounds like a security issue tbh so this could get fixed in the future but here are more details on said workaround:
Make a request to the URL which is supposed to trigger virtual smartcard unlock to access the user certificate, but with the HttpClient at native level (C#)
I've created another Windows Runtime Component in my solution which does a simple POST to the url I want to access from InAppBrowser later on.
While setting up the Windows.Web.Http.HttpClient, I fetch the user certificate from the smartcard and set it as HttpBaseProtocolFilter.ClientCertificate.
public sealed class SSLHelper
{
private static String errorMessage = "";
private static String statusMessage = "";
public static IAsyncOperation<Boolean> establishSSLConnection(String url)
{
return connect(url).AsAsyncOperation<Boolean>();
}
public static String getErrorMessage()
{
return SSLHelper.errorMessage;
}
public static String getStatusMessage()
{
return SSLHelper.statusMessage;
}
private static async Task<Boolean> connect(String urlString)
{
Certificate clientCert = await getCertificateAsync();
HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
filter.ClientCertificate = clientCert;
HttpClient client = new HttpClient(filter);
try
{
System.Uri url = new System.Uri(urlString);
HttpResponseMessage response = await client.PostAsync(url, new HttpStringContent(""));
response.EnsureSuccessStatusCode();
SSLHelper.statusMessage = response.StatusCode.ToString();
return true;
}
catch (Exception e)
{
SSLHelper.errorMessage = e.ToString();
return false;
}
}
private static async Task<Certificate> getCertificateAsync()
{
CertificateQuery query = new CertificateQuery();
query.IssuerName = "Sample Issuer";
IReadOnlyList<Certificate> certStores = await CertificateStores.FindAllAsync(query);
return certStores.FirstOrDefault<Certificate>();
}
}
Make that code return as a promise on Javascript level and once it resolves, start the code which uses InAppBrowser to access the secure URL again. The native request causes the PIN prompt for virtual smartcard access, once you have entered the correct PIN, InAppBrowser / WebView can magically establish the connection.
I want to start/resume and stop/suspend instances on google compute engine, but it gives "java.lang.UnsupportedOperationException".Is there any alternative way
to perform these operations?
public class Example {
public static void main(String[] args)
{
String provider = "google-compute-engine";
String identity = "****#developer.gserviceaccount.com";
String credential = "path to private key";
String groupName = "newgroup";
credential = getCredentialFromJsonKeyFile(credential);
Iterable<Module> modules = ImmutableSet.<Module> of(
new SshjSshClientModule(),
new SLF4JLoggingModule(),
new EnterpriseConfigurationModule());
ContextBuilder builder = ContextBuilder.newBuilder(provider)
.credentials(identity, credential)
.modules(modules);
ComputeService compute=builder.buildView(ComputeServiceContext.class).getComputeService();
compute.suspendNode("Instance id");
//compute.suspendNodesMatching(Predicates.<NodeMetadata> and(inGroup(groupName)));
System.out.println("suspended");
compute.getContext().close();
}
private static String getCredentialFromJsonKeyFile(String filename) {
try {
String fileContents = Files.toString(new File(filename), UTF_8);
Supplier<Credentials> credentialSupplier = new GoogleCredentialsFromJson(fileContents);
String credential = credentialSupplier.get().credential;
return credential;
} catch (IOException e) {
System.err.println("Exception reading private key from '%s': " + filename);
e.printStackTrace();
System.exit(1);
return null;
}
}
}
Output:
suspending node(node id)
Exception in thread "main" java.lang.UnsupportedOperationException: suspend is not supported by GCE
at org.jclouds.googlecomputeengine.compute.GoogleComputeEngineServiceAdapter.suspendNode(GoogleComputeEngineServiceAdapter.java:251)
at org.jclouds.compute.strategy.impl.AdaptingComputeServiceStrategies.suspendNode(AdaptingComputeServiceStrategies.java:171)
at org.jclouds.compute.internal.BaseComputeService.suspendNode(BaseComputeService.java:503)
at org.jclouds.examples.compute.basics.Example.main(Example.java:79)
It is not directly supported in the portable jclouds ComputeService, but from the ComputeServiceContext you can get the GoogleComputeEngineApi and the InstanceApi, and use the start/stop methods in there.
FYI, there is an ongoing patch to add support for the start/stop operations in the ComputeService: https://github.com/jclouds/jclouds-labs-google/pull/141
You can stop an instance from the API.
POST https://www.googleapis.com/compute/v1/projects/<project>/zones/<zone>/instances/<instance>/stop
Where :
project in URL is you project id.
zone in URL is the name of zone for the request.
instance in URL is the name of instances to stop.
Here's the docs
I am the admin of an enterprise account at Box, and I'm working on an automated integration to update our users' email addresses and set their quotas, based on our enterprise' internal catalog.
Although the Box API documentation seems targeted at other usage scenarios, I can gather that once I get an access_token/refresh_token pair, that refresh_token is valid for 60 days, and I can get a new one at any time during that period.
Being of the conviction that "something always goes wrong", I'm just wondering if there is any way of automating the initial step of getting an access_token/refresh_token pair, that doesn't require a browser and manual interaction. I'm afraid that IF the refresh_token is lost or becomes invalid due to an update at Box or similar, no one here will remember how you went about getting that initial token pair by hand.
If there isn't a way to do it automatically, I'll just live with it, but I don't want to give up without having asked explicitly to know that I didn't just miss something. :-)
[Is there] any way of automating the initial step of getting an access_token/refresh_token pair, that doesn't require a browser and manual interaction
No, there are no authZ/authN shortcuts. That goes double for accounts that can manage an entire enterprise, given their power and reach.
I'm afraid ... no one here will remember how you went about getting that initial token pair by hand.
One way to resolve this might be to implement something like this:
Create a Box app with the 'manage an enterprise' scope.
Create a web app in your domain that simply implements the OAuth2 workflow.
Store the resulting access/refresh token pair in your persistence layer of choice
If/when something goes wrong due to authZ/authN issues, have your script notify a group email account that someone needs to go to the web app and request a new token.
There are sample web apps available to help get you started. (Python, Asp.NET MVC)
... The Box API documentation seems targeted at other usage scenarios...
A lot of the enterprise-specific stuff is found in the Users and Events parts of the API, and the As-User feature makes the entire API enterprise-ready. It's pretty neat.
You can build a workarround with an webclient like this:
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.ExecutionException;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.ElementNotFoundException;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlButton;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput;
import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
import com.gargoylesoftware.htmlunit.html.HtmlTextInput;
public class BoxAuth {
private String key;
private String email;
private String password;
private String redirectUrl;
private final String AUTH_URL;
public BoxAuth(String key, String email, String password, String redirectUrl) {
super();
this.key = key;
this.email = email;
this.password = password;
this.redirectUrl = redirectUrl;
this.AUTH_URL = "https://www.box.com/api/oauth2/authorize?response_type=code&client_id=" + key + "&redirect_uri=" + this.redirectUrl;
}
public String authorize() throws IOException, InterruptedException, ExecutionException {
System.out.println("AUTHORIZING: " + AUTH_URL);
final WebClient webClient = new WebClient(BrowserVersion.FIREFOX_17);
HtmlPage loginPage = webClient.getPage(AUTH_URL);
final HtmlPage grantAccessePage = this.authorizeLogin(loginPage);
return this.authorizeGrantAccess(grantAccessePage);
}
private HtmlPage authorizeLogin(HtmlPage page) throws IOException {
final HtmlForm loginForm = page.getFormByName("login_form");
loginForm.getInputByName("password");
final HtmlTextInput emailField = (HtmlTextInput) loginForm.getInputByName("login");
emailField.setValueAttribute(this.email);
final HtmlPasswordInput passwordField = (HtmlPasswordInput) loginForm.getInputByName("password");
passwordField.setValueAttribute(this.password);
final HtmlSubmitInput loginButton = loginForm.getInputByName("login_submit");
final HtmlPage result = loginButton.click();
try {
final HtmlForm test = result.getFormByName("login_form");
throw new Exception("BoxAPI: Wrong login data!!!");
} catch (ElementNotFoundException e) {
}
return result;
}
private String authorizeGrantAccess(HtmlPage grantAccessePage) throws IOException, InterruptedException, ExecutionException {
final HtmlForm grantAccessForm = grantAccessePage.getHtmlElementById("consent_form");
final HtmlButton grantAccess = grantAccessForm.getButtonByName("consent_accept");
final HtmlPage codePage = grantAccess.click();
URL url = codePage.getUrl();
String result = "";
if (url.toString().contains("&code=")) {
result = url.toString().substring(url.toString().indexOf("&code="));
result = result.replace("&code=", "");
}
return result;
}
}
as redirect_url u can use something like "https://app.box.com/services/yourservice"
I am developing a feature which need to create a new empty file(not document) to Google drive, now I am using document list API 3.0 and I am referring to the document: https://developers.google.com/google-apps/documents-list/#uploading_a_new_document_or_file_with_both_metadata_and_content.
I will upload a zero byte file to the Google drive to generate the empty file.
Now I have a problem during request step 1 and request step 2. After the first Post[resumable-create-media link] request I successfully got the upload location. Then when I request put method to the location, I got a 404 not found error. All of the requests have "GData-Version: 3.0" and "Authorization: accessToken" headers.
I searched a lot from the forum and figured out how to create empty document but could not figure out how to create empty file. Here is my code, could anybody help to see which part is wrong? Thanks in advance.
private final static String PARAM_CONTENT_TYPE = "Content-Type";
private final static String PARAM_UPLOAD_LENGTH = "X-Upload-Content-Length";
private final static String PARAM_UPLOAD_TYPE = "X-Upload-Content-Type";
private final static String CONTENT_TYPE_XML = "application/atom+xml";
private final static String URI_RESUMABLE_RESOURCE = "https://docs.google.com/feeds/upload/create-session/default/private/full";
private final static String ENTITY_NEW_FILE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><entry xmlns=\"http://www.w3.org/2005/Atom\" "
+ "xmlns:docs=\"http://schemas.google.com/docs/2007\"><title>{0}</title></entry>";
#Override
public boolean createNewFile() throws IOException {
String uri = null;
if (ROOT.equals(parentResourceId))
uri = URI_RESUMABLE_RESOURCE;
else
uri = URI_RESUMABLE_RESOURCE + "/%3A" + parentResourceId + "/contents";
try {
StringEntity entity = new StringEntity(MessageFormat.format(ENTITY_NEW_FILE, getName()), Constants.ENCODING);
Map<String, String> headers = new HashMap<String, String>();
headers.put(PARAM_CONTENT_TYPE, CONTENT_TYPE_XML);
headers.put(PARAM_UPLOAD_LENGTH, "0");
headers.put(PARAM_UPLOAD_TYPE, "text/plain");
Map<String, String> params = new HashMap<String, String>();
params.put("convert", "false");
HttpResponse response = helper.execMethodAsResponse(uri, new PostMethod(entity), headers, params);
String location = null;
if ((location = response.getFirstHeader("Location").getValue()) != null) {
headers = new HashMap<String, String>();
headers.put(PARAM_CONTENT_TYPE, "text/plain");
headers.put("Content-Range", "bytes 0-0/0");
//FIXME: Problem occurs here, this put invocation will return 404 not found error.
JsonObject obj = helper.execMethodAsJson(location, new PutMethod(new ByteArrayEntity(new byte[0])), headers, null);
if (obj != null) {
decorateFile(this, obj.get("entry").getAsJsonObject());
return true;
}
}
} catch (UnsupportedEncodingException e) {
}
return false;
}
The easiest way to create an empty file on Google Drive is to use the Google Drive API v2 and send a service.files.insert(File content) request.
You can use the Java sample from the Reference Guide and edit it to not include a MediaContent: https://developers.google.com/drive/v2/reference/files/insert
I have figured out the problem, according to this link: Google Documents List API file upload 400 response
The second put request does not need any additional headers such as "GData-Version: 3.0" and "Authorization: Bearer ****", etc. The only thing need to do is new a put request with the location URI, then it will return response with 201 status code.
Thanks all.