Bug 492968 - [USS] Provide a way to avoid the secure storage master password dialog

"Interactive" flag for the OAuth credentials provider to support
non-interactive operation and fail silently if that's not possible.

Bug: 492968
Change-Id: I249921bdcf91c8fdce78ce7010df28c89bbae0e2
Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=492968
Signed-off-by: Carsten Reckord <reckord@yatta.de>
Signed-off-by: Brian de Alwis <bsd@mt.ca>
diff --git a/org.eclipse.userstorage.oauth.tests/src/org/eclipse/userstorage/oauth/EclipseOAuthCredentialsProviderTest.java b/org.eclipse.userstorage.oauth.tests/src/org/eclipse/userstorage/oauth/EclipseOAuthCredentialsProviderTest.java
index e18dc48..87b4109 100644
--- a/org.eclipse.userstorage.oauth.tests/src/org/eclipse/userstorage/oauth/EclipseOAuthCredentialsProviderTest.java
+++ b/org.eclipse.userstorage.oauth.tests/src/org/eclipse/userstorage/oauth/EclipseOAuthCredentialsProviderTest.java
@@ -1,21 +1,30 @@
 package org.eclipse.userstorage.oauth;
 
-import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+
+import org.eclipse.userstorage.IStorageService;
+import org.eclipse.userstorage.internal.Session;
+import org.eclipse.userstorage.internal.oauth.UIFacade;
+import org.eclipse.userstorage.spi.Credentials;
+
+import org.apache.http.client.fluent.Request;
+import org.hamcrest.CoreMatchers;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
 
 import java.net.URI;
 import java.net.URISyntaxException;
 
-import org.apache.http.client.fluent.Request;
-import org.eclipse.userstorage.internal.Session;
-import org.eclipse.userstorage.spi.Credentials;
-import org.junit.Test;
-
 public class EclipseOAuthCredentialsProviderTest {
 
 	@Test
@@ -37,24 +46,25 @@
 		assertFalse(provider.isValid(new Credentials("username", "")));
 		assertFalse(provider.isValid(new Credentials("username", "blah")));
 	}
-	
+
 	@Test
 	public void testIsValid_ExpiredCredentials() throws URISyntaxException {
 		EclipseOAuthCredentialsProvider provider = new EclipseOAuthCredentialsProvider("clientId", "clientSecret",
-					new String[] { "scope1", "scope2" }, new URI("http://localhost"));
+				new String[] { "scope1", "scope2" }, new URI("http://localhost"));
 		// expired token
-		assertFalse(provider.isValid(new Credentials("username", "{\"access_token\":\"a8f8a49232520e7d621b1ae1235a35665e27344d\","
-				+ "\"token_type\":\"Bearer\"," + "\"scope\":\"scope1\","
-						+ "\"_expires_\":\"Mon, 07 Nov 2016 22:19:17 GMT\"}")));
+		assertFalse(provider.isValid(new Credentials("username",
+				"{\"access_token\":\"a8f8a49232520e7d621b1ae1235a35665e27344d\"," + "\"token_type\":\"Bearer\","
+						+ "\"scope\":\"scope1\"," + "\"_expires_\":\"Mon, 07 Nov 2016 22:19:17 GMT\"}")));
 	}
 
 	@Test
 	public void testIsValid_InsufficientScope() throws URISyntaxException {
 		EclipseOAuthCredentialsProvider provider = new EclipseOAuthCredentialsProvider("clientId", "clientSecret",
-					new String[] { "scope1", "scope2" }, new URI("http://localhost"));
+				new String[] { "scope1", "scope2" }, new URI("http://localhost"));
 		// expired token
-		assertFalse(provider.isValid(new Credentials("username", "{\"access_token\":\"a8f8a49232520e7d621b1ae1235a35665e27344d\","
-				+ "\"token_type\":\"Bearer\"," + "\"scope\":\"scope1\"}")));
+		assertFalse(provider
+				.isValid(new Credentials("username", "{\"access_token\":\"a8f8a49232520e7d621b1ae1235a35665e27344d\","
+						+ "\"token_type\":\"Bearer\"," + "\"scope\":\"scope1\"}")));
 	}
 
 	@Test
@@ -64,15 +74,50 @@
 		Credentials credentials = new Credentials("username", serializedForm);
 
 		EclipseOAuthCredentialsProvider provider = new EclipseOAuthCredentialsProvider("clientId", "clientSecret",
-				new String[] {"scope1"}, new URI("http://localhost"));
+				new String[] { "scope1" }, new URI("http://localhost"));
 		assertTrue(provider.isValid(credentials));
 
 		Request request = provider.configureRequest(Request.Get("http://localhost"), new URI("http://nonsensical"),
 				credentials);
 		assertNotNull(request);
 		assertThat(Session.formatRequest(request),
-				containsString("Authorization: Bearer a8f8a49232520e7d621b1ae1235a35665e27344d"));
+				CoreMatchers.containsString("Authorization: Bearer a8f8a49232520e7d621b1ae1235a35665e27344d"));
+	}
+
+	@Test
+	public void testProvideCredentials_nonInteractive() {
+		IStorageService service = Mockito.mock(IStorageService.class);
+
+		EclipseOAuthCredentialsProvider provider = new EclipseOAuthCredentialsProvider(
+				URI.create("http://does.not.exist"), "clientId", "clientSecret", new String[] { "scope1" },
+				URI.create("http://localhost"));
+		provider.uiFacade = Mockito.mock(UIFacade.class);
+
+		provider.setInteractive(false);
+		Credentials credential = provider.provideCredentials(service, false);
+		Mockito.verifyZeroInteractions(provider.uiFacade);
+		assertNull(credential); // should fast-exit
+	}
+
+	@Test
+	public void testProvideCredentials_interactive() {
+		IStorageService service = Mockito.mock(IStorageService.class);
+		Mockito.when(service.getServiceLabel()).thenReturn("provider");
+
+		URI authServiceURI = URI.create("http://does.not.exist");
+		URI callbackURI = URI.create("http://localhost");
+		EclipseOAuthCredentialsProvider provider = new EclipseOAuthCredentialsProvider(authServiceURI, "clientId",
+				"clientSecret", new String[] { "scope1" }, callbackURI);
+		provider.uiFacade = Mockito.mock(UIFacade.class);
+		Mockito.when(provider.uiFacade.obtainAuthCode(anyString(), any(URI.class), any(URI.class))).thenReturn(null);
+
+		assertTrue(provider.isInteractive());
+		Credentials credential = provider.provideCredentials(service, false);
+
+		assertNull(credential); // facade.obtainAuthCode => null returns null
+		ArgumentCaptor<URI> authCaptor = ArgumentCaptor.forClass(URI.class);
+		Mockito.verify(provider.uiFacade).obtainAuthCode(eq("provider"), authCaptor.capture(), eq(callbackURI));
+		assertThat(authCaptor.getValue().toString(), CoreMatchers.startsWith(authServiceURI.toString()));
 	}
 
 }
-
diff --git a/org.eclipse.userstorage.oauth/src/org/eclipse/userstorage/oauth/EclipseOAuthCredentialsProvider.java b/org.eclipse.userstorage.oauth/src/org/eclipse/userstorage/oauth/EclipseOAuthCredentialsProvider.java
index 03ab911..5b23910 100644
--- a/org.eclipse.userstorage.oauth/src/org/eclipse/userstorage/oauth/EclipseOAuthCredentialsProvider.java
+++ b/org.eclipse.userstorage.oauth/src/org/eclipse/userstorage/oauth/EclipseOAuthCredentialsProvider.java
@@ -101,6 +101,8 @@
 
   UIFacade uiFacade; // package protected for testing purposes
 
+  private boolean interactive = true;
+
   /**
    * @param clientId the OAuth identifier assigned to the application
    * @param clientSecret the OAuth secret assigned to the application
@@ -159,6 +161,11 @@
           return asCredentials(service, authToken);
         }
       }
+      if (!isInteractive())
+      {
+        debug("Non-interactive login process failed");
+        return null;
+      }
 
       /* Start the full process */
       debug("Starting OAuth authorization process");
@@ -510,4 +517,14 @@
       .build();
     //@formatter:on
   }
+
+  public boolean isInteractive()
+  {
+    return interactive;
+  }
+
+  public void setInteractive(boolean interactive)
+  {
+    this.interactive = interactive;
+  }
 }