added OAuth2 authentication for REST protocol

Change-Id: I3b8f3943ea043587730870a0b861760a4d6f3aa7
diff --git a/lib/BUCK b/lib/BUCK
index ccc1e15..5ff711f 100644
--- a/lib/BUCK
+++ b/lib/BUCK
@@ -548,6 +548,15 @@
 )
 
 remote_jar (
+  name = 'jersey-security',
+  out = 'oauth2-client-2.25.1.jar',
+  url = 'mvn:org.glassfish.jersey.security:oauth2-client:jar:2.25.1',
+  sha1 = '5081be1cdc45a48ebeada89157cab4711f7bad1b',
+  maven_coords = 'org.glassfish.jersey.security:oauth2-client:jar:NON-OSGI:2.25.1',
+  visibility = [ 'PUBLIC' ],
+)
+
+remote_jar (
   name = 'jersey-common',
   out = 'jersey-common-2.25.1.jar',
   url = 'mvn:org.glassfish.jersey.core:jersey-common:jar:2.25.1',
diff --git a/lib/deps.json b/lib/deps.json
index 7fae453..f1ff1e9 100644
--- a/lib/deps.json
+++ b/lib/deps.json
@@ -155,6 +155,7 @@
     "javax.inject": "mvn:org.glassfish.hk2.external:javax.inject:2.5.0-b32",
     "javax.ws.rs-api": "mvn:javax.ws.rs:javax.ws.rs-api:2.1",
     "jersey-client": "mvn:org.glassfish.jersey.core:jersey-client:2.25.1",
+    "jersey-security": "mvn:org.glassfish.jersey.security:oauth2-client:jar:2.25.1",
     "jersey-common": "mvn:org.glassfish.jersey.core:jersey-common:2.25.1",
     "jersey-container-jetty-http": "mvn:org.glassfish.jersey.containers:jersey-container-jetty-http:2.25.1",
     "jersey-container-servlet": "mvn:org.glassfish.jersey.containers:jersey-container-servlet:2.25.1",
diff --git a/lib/pom.xml b/lib/pom.xml
index 12045a9..3398de1 100644
--- a/lib/pom.xml
+++ b/lib/pom.xml
@@ -223,6 +223,13 @@
                 <artifactId>jersey-client</artifactId>
                 <version>${jersey.version}</version>
             </dependency>
+
+            <dependency>
+                <groupId>org.glassfish.jersey.security</groupId>
+                <artifactId>oauth2-client</artifactId>
+                <version>${jersey.version}</version>
+            </dependency>
+
             <dependency>
                 <groupId>org.glassfish.jersey.containers</groupId>
                 <artifactId>jersey-container-servlet</artifactId>
diff --git a/protocols/rest/api/BUCK b/protocols/rest/api/BUCK
index 74f33b2..471084f 100644
--- a/protocols/rest/api/BUCK
+++ b/protocols/rest/api/BUCK
@@ -3,6 +3,7 @@
     '//utils/rest:onlab-rest',
     '//lib:CORE_DEPS',
     '//lib:jersey-client',
+    '//lib:jersey-security',
     '//lib:jersey-common',
     '//lib:httpclient-osgi',
     '//lib:httpcore-osgi',
diff --git a/protocols/rest/api/pom.xml b/protocols/rest/api/pom.xml
index ff5f7de..e334e7a 100644
--- a/protocols/rest/api/pom.xml
+++ b/protocols/rest/api/pom.xml
@@ -35,6 +35,12 @@
             <groupId>org.glassfish.jersey.core</groupId>
             <artifactId>jersey-client</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth2-client</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>org.apache.httpcomponents</groupId>
             <artifactId>httpclient-osgi</artifactId>
@@ -43,7 +49,6 @@
         <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
-            <version>2.4</version>
         </dependency>
         <dependency>
           <groupId>junit</groupId>
diff --git a/protocols/rest/api/src/main/java/org/onosproject/protocol/http/ctl/HttpSBControllerImpl.java b/protocols/rest/api/src/main/java/org/onosproject/protocol/http/ctl/HttpSBControllerImpl.java
index 4a893fcb..602d989 100644
--- a/protocols/rest/api/src/main/java/org/onosproject/protocol/http/ctl/HttpSBControllerImpl.java
+++ b/protocols/rest/api/src/main/java/org/onosproject/protocol/http/ctl/HttpSBControllerImpl.java
@@ -25,10 +25,12 @@
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.ssl.SSLContextBuilder;
 import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.glassfish.jersey.client.oauth2.OAuth2ClientSupport;
 import org.onlab.packet.IpAddress;
 import org.onosproject.net.DeviceId;
 import org.onosproject.protocol.http.HttpSBController;
 import org.onosproject.protocol.rest.RestSBDevice;
+import org.onosproject.protocol.rest.RestSBDevice.AuthenticationScheme;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -55,6 +57,8 @@
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 /**
  * The implementation of HttpSBController.
  */
@@ -102,11 +106,7 @@
     public void addDevice(RestSBDevice device) {
         if (!deviceMap.containsKey(device.deviceId())) {
             Client client = ignoreSslClient();
-            if (device.username() != null) {
-                String username = device.username();
-                String password = device.password() == null ? "" : device.password();
-                authenticate(client, username, password);
-            }
+            authenticate(client, device);
             clientMap.put(device.deviceId(), client);
             deviceMap.put(device.deviceId(), device);
         } else {
@@ -283,8 +283,23 @@
         }
     }
 
-    private void authenticate(Client client, String username, String password) {
-        client.register(HttpAuthenticationFeature.basic(username, password));
+    private void authenticate(Client client, RestSBDevice device) {
+        AuthenticationScheme authScheme = device.authentication();
+        if (authScheme == AuthenticationScheme.NO_AUTHENTICATION) {
+            log.debug("{} scheme is specified, ignoring authentication", authScheme);
+            return;
+        } else if (authScheme == AuthenticationScheme.OAUTH2) {
+            String token = checkNotNull(device.token());
+            client.register(OAuth2ClientSupport.feature(token));
+        } else if (authScheme == AuthenticationScheme.BASIC) {
+            String username = device.username();
+            String password = device.password() == null ? "" : device.password();
+            client.register(HttpAuthenticationFeature.basic(username, password));
+        } else {
+            // TODO: Add support for other authentication schemes here.
+            throw new IllegalArgumentException(String.format("Unsupported authentication scheme: %s",
+                    authScheme.name()));
+        }
     }
 
     protected WebTarget getWebTarget(DeviceId device, String request) {
@@ -343,12 +358,15 @@
         try {
             sslcontext = SSLContext.getInstance("TLS");
             sslcontext.init(null, new TrustManager[]{new X509TrustManager() {
+                @Override
                 public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                 }
 
+                @Override
                 public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                 }
 
+                @Override
                 public X509Certificate[] getAcceptedIssuers() {
                     return new X509Certificate[0];
                 }
diff --git a/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/DefaultRestSBDevice.java b/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/DefaultRestSBDevice.java
index 8f59093..fef7f37 100644
--- a/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/DefaultRestSBDevice.java
+++ b/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/DefaultRestSBDevice.java
@@ -41,6 +41,8 @@
     private String protocol;
     private String url;
     private boolean isProxy;
+    private AuthenticationScheme authenticationScheme;
+    private String token;
     private final Optional<String> testUrl;
     private final Optional<String> manufacturer;
     private final Optional<String> hwVersion;
@@ -48,13 +50,13 @@
 
     public DefaultRestSBDevice(IpAddress ip, int port, String name, String password,
                                String protocol, String url, boolean isActive) {
-        this(ip, port, name, password, protocol, url, isActive, "", "", "", "");
+        this(ip, port, name, password, protocol, url, isActive, "", "", "", "", AuthenticationScheme.BASIC, "");
     }
 
     public DefaultRestSBDevice(IpAddress ip, int port, String name, String password,
                                String protocol, String url, boolean isActive, String testUrl, String manufacturer,
-                               String hwVersion,
-                               String swVersion) {
+                               String hwVersion, String swVersion, AuthenticationScheme authenticationScheme,
+                               String token) {
         Preconditions.checkNotNull(ip, "IP address cannot be null");
         Preconditions.checkArgument(port > 0, "Port address cannot be negative");
         Preconditions.checkNotNull(protocol, "protocol address cannot be null");
@@ -65,6 +67,8 @@
         this.isActive = isActive;
         this.protocol = protocol;
         this.url = StringUtils.isEmpty(url) ? null : url;
+        this.authenticationScheme = authenticationScheme;
+        this.token = token;
         this.manufacturer = StringUtils.isEmpty(manufacturer) ?
                 Optional.empty() : Optional.ofNullable(manufacturer);
         this.hwVersion = StringUtils.isEmpty(hwVersion) ?
@@ -159,6 +163,16 @@
     }
 
     @Override
+    public AuthenticationScheme authentication() {
+        return authenticationScheme;
+    }
+
+    @Override
+    public String token() {
+        return token;
+    }
+
+    @Override
     public String toString() {
         return MoreObjects.toStringHelper(this)
                 .omitNullValues()
@@ -168,6 +182,8 @@
                 .add("username", username)
                 .add("port", port)
                 .add("ip", ip)
+                .add("authentication", authenticationScheme.name())
+                .add("token", token)
                 .add("manufacturer", manufacturer.orElse(null))
                 .add("hwVersion", hwVersion.orElse(null))
                 .add("swVersion", swVersion.orElse(null))
diff --git a/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/RestSBDevice.java b/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/RestSBDevice.java
index edcce6e..6767b2d 100644
--- a/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/RestSBDevice.java
+++ b/protocols/rest/api/src/main/java/org/onosproject/protocol/rest/RestSBDevice.java
@@ -26,6 +26,16 @@
  */
 public interface RestSBDevice {
     /**
+     * REST Authentication schemes.
+     */
+    public enum AuthenticationScheme {
+        NO_AUTHENTICATION,
+        BASIC,
+        OAUTH,
+        OAUTH2,
+    }
+
+    /**
      * Returns the ip of this device.
      *
      * @return ip
@@ -40,6 +50,20 @@
     int port();
 
     /**
+     * The authentication scheme of rest device.
+     *
+     * @return authentication
+     */
+    AuthenticationScheme authentication();
+
+    /**
+     * The access token of rest device if authentication is OAuth2.
+     *
+     * @return token
+     */
+    String token();
+
+    /**
      * Returns the username of this device.
      *
      * @return username
@@ -91,6 +115,7 @@
     /**
      * Returns the proxy state of this device
      * (if true, the device is proxying multiple ONOS devices).
+     *
      * @return proxy state
      */
     boolean isProxy();
@@ -122,4 +147,5 @@
      * @return the software version.
      */
     Optional<String> swVersion();
+
 }
diff --git a/providers/rest/BUCK b/providers/rest/BUCK
index 81fe5b4..b6e2e2b 100644
--- a/providers/rest/BUCK
+++ b/providers/rest/BUCK
@@ -3,6 +3,7 @@
     '//protocols/rest/api:onos-protocols-rest-api',
     '//protocols/rest/ctl:onos-protocols-rest-ctl',
     '//lib:jersey-client',
+    '//lib:jersey-security',
     '//lib:commons-io',
     '//lib:httpclient-osgi',
     '//lib:httpcore-osgi',
diff --git a/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceConfig.java b/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceConfig.java
index 648e56a..cd290e5 100644
--- a/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceConfig.java
+++ b/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceConfig.java
@@ -21,6 +21,7 @@
 import org.onlab.packet.IpAddress;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.config.Config;
+import org.onosproject.protocol.rest.RestSBDevice.AuthenticationScheme;
 
 /**
  * Configuration to push devices to the REST provider.
@@ -38,11 +39,14 @@
     private static final String MANUFACTURER = "manufacturer";
     private static final String HWVERSION = "hwVersion";
     private static final String SWVERSION = "swVersion";
+    private static final String AUTHENTICATION_SCHEME = "authenticationScheme";
+    private static final String TOKEN = "token";
 
     @Override
     public boolean isValid() {
         return hasOnlyFields(IP, PORT, USERNAME, PASSWORD, PROTOCOL, URL,
-                             TESTURL, MANUFACTURER, HWVERSION, SWVERSION) &&
+                TESTURL, MANUFACTURER, HWVERSION, SWVERSION, AUTHENTICATION_SCHEME,
+                TOKEN) &&
                 ip() != null;
     }
 
@@ -136,6 +140,31 @@
         return get(SWVERSION, "");
     }
 
+    /**
+     * Gets the authentication type of the REST device.
+     * Default is 'basic' if username is defined, else default is no_authentication.
+     *
+     * @return authentication
+     */
+    public AuthenticationScheme authenticationScheme() {
+        // hack for backward compatibility
+        if (!hasField(AUTHENTICATION_SCHEME)) {
+            if (hasField(USERNAME)) {
+                return AuthenticationScheme.BASIC;
+            }
+        }
+        return AuthenticationScheme.valueOf(get(AUTHENTICATION_SCHEME, "NO_AUTHENTICATION").toUpperCase());
+    }
+
+    /**
+     * Gets the token of the REST device.
+     *
+     * @return token
+     */
+    public String token() {
+        return get(TOKEN, "");
+    }
+
     private Pair<String, Integer> extractIpPort() {
         String info = subject.toString();
         if (info.startsWith(RestDeviceProvider.REST)) {
diff --git a/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceProvider.java b/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceProvider.java
index 091dc34..7f6d9e8 100644
--- a/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceProvider.java
+++ b/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceProvider.java
@@ -28,8 +28,6 @@
 import org.onlab.util.SharedScheduledExecutors;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
-import org.onosproject.net.behaviour.PortAdmin;
-import org.onosproject.net.config.ConfigException;
 import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.Device;
@@ -38,7 +36,9 @@
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.SparseAnnotations;
 import org.onosproject.net.behaviour.DevicesDiscovery;
+import org.onosproject.net.behaviour.PortAdmin;
 import org.onosproject.net.behaviour.PortDiscovery;
+import org.onosproject.net.config.ConfigException;
 import org.onosproject.net.config.ConfigFactory;
 import org.onosproject.net.config.NetworkConfigEvent;
 import org.onosproject.net.config.NetworkConfigListener;
@@ -73,6 +73,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -81,7 +82,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.stream.Collectors;
-import java.util.concurrent.CompletableFuture;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.onlab.util.Tools.groupedThreads;
@@ -354,22 +354,25 @@
         Set<DeviceId> deviceSubjects =
                 cfgService.getSubjects(DeviceId.class, RestDeviceConfig.class);
         connectDevices(deviceSubjects.stream()
-                               .filter(deviceId -> deviceService.getDevice(deviceId) == null)
-                               .map(deviceId -> {
-                                   RestDeviceConfig config =
-                                           cfgService.getConfig(deviceId, RestDeviceConfig.class);
-                                   return new DefaultRestSBDevice(config.ip(),
-                                                                  config.port(),
-                                                                  config.username(),
-                                                                  config.password(),
-                                                                  config.protocol(),
-                                                                  config.url(),
-                                                                  false,
-                                                                  config.testUrl(),
-                                                                  config.manufacturer(),
-                                                                  config.hwVersion(),
-                                                                  config.swVersion());
-                               }).collect(Collectors.toSet()));
+                .filter(deviceId -> deviceService.getDevice(deviceId) == null)
+                .map(deviceId -> {
+                    RestDeviceConfig config =
+                            cfgService.getConfig(deviceId, RestDeviceConfig.class);
+                    return new DefaultRestSBDevice(config.ip(),
+                            config.port(),
+                            config.username(),
+                            config.password(),
+                            config.protocol(),
+                            config.url(),
+                            false,
+                            config.testUrl(),
+                            config.manufacturer(),
+                            config.hwVersion(),
+                            config.swVersion(),
+                            config.authenticationScheme(),
+                            config.token()
+                    );
+                }).collect(Collectors.toSet()));
     }
 
     //Old method to register devices provided via net-cfg under apps/rest/ tree
diff --git a/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestProviderConfig.java b/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestProviderConfig.java
index e04be6f..21d9774 100644
--- a/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestProviderConfig.java
+++ b/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestProviderConfig.java
@@ -21,15 +21,17 @@
 import com.google.common.collect.Sets;
 import org.onlab.packet.IpAddress;
 import org.onosproject.core.ApplicationId;
-import org.onosproject.net.config.ConfigException;
 import org.onosproject.net.config.Config;
+import org.onosproject.net.config.ConfigException;
 import org.onosproject.protocol.rest.DefaultRestSBDevice;
 import org.onosproject.protocol.rest.RestSBDevice;
+import org.onosproject.protocol.rest.RestSBDevice.AuthenticationScheme;
 
 import java.util.Set;
 
 /**
  * Configuration for RestSB provider.
+ *
  * @deprecated 1.10.0 Kingfisher. Please Use RestDeviceConfig
  */
 @Deprecated
@@ -48,6 +50,8 @@
     private static final String MANUFACTURER = "manufacturer";
     private static final String HWVERSION = "hwVersion";
     private static final String SWVERSION = "swVersion";
+    private static final String AUTHENTICATION_SCHEME = "authenticationScheme";
+    private static final String TOKEN = "token";
 
     public Set<RestSBDevice> getDevicesAddresses() throws ConfigException {
         Set<RestSBDevice> devicesAddresses = Sets.newHashSet();
@@ -65,11 +69,14 @@
                 String manufacturer = node.path(MANUFACTURER).asText();
                 String hwVersion = node.path(HWVERSION).asText();
                 String swVersion = node.path(SWVERSION).asText();
+                AuthenticationScheme authenticationScheme = AuthenticationScheme.valueOf(node.path(
+                        AUTHENTICATION_SCHEME).asText().toUpperCase());
+                String token = node.path(TOKEN).asText();
 
                 devicesAddresses.add(new DefaultRestSBDevice(ipAddr, port, username,
-                                                             password, protocol,
-                                                             url, false, testUrl, manufacturer,
-                                                             hwVersion, swVersion));
+                        password, protocol,
+                        url, false, testUrl, manufacturer,
+                        hwVersion, swVersion, authenticationScheme, token));
             }
         } catch (IllegalArgumentException e) {
             throw new ConfigException(CONFIG_VALUE_ERROR, e);
diff --git a/web/api/BUCK b/web/api/BUCK
index 69c1cbf..c1c6ab4 100644
--- a/web/api/BUCK
+++ b/web/api/BUCK
@@ -10,6 +10,7 @@
 TEST_DEPS = [
     '//lib:TEST_REST',
     '//lib:minimal-json',
+    '//lib:jersey-security'
 ]
 
 osgi_jar_with_tests (